java 一个java 多线程内存溢出占多少内存

用户名:LinkedKeeper
文章数:80
评论数:54
访问量:66052
注册日期:
阅读量:1297
阅读量:3317
阅读量:449869
阅读量:1134539
51CTO推荐博文
1.Java内存模型Java内存模型用来屏蔽各种硬件和操作系统的内存访问差异,以实现在各种平台下都能达到一致的并发效果。主内存与工作内存Java内存模型的主要目标是/定义程序中各个变量的/访问规则,即在虚拟机中/将变量存储到内存和从内存中取出变量这样的底层细节。Java内存模型规定:所有的变量都存储在主内存中;每个线程有自己的工作内存,线程的工作内存保存被线程使用到变量的主内存副本拷贝;线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存的变量;不同线程之间不能直接访问对方工作内存中的变量,线程间变量值的传递通过主内存来完成。内存间交互操作Java内存模型定义了八种操作:lock(锁定):作用于主内存的变量,它把一个变量标识为一个线程独占的状态;unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;read(读取):作用于主内存的变量,它把一个变量的值从主内存传送到线程中的工作内存,以便随后的load动作使用;load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中;use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎;assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存中的变量;store(存储):作用于工作内存的变量,它把工作内存中的一个变量的值传送到主内存中,以便随后的write操作;write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值写入主内存的变量中。操作时规则不允许read和load、store和write操作之一单独出现;说明:如果要把一个变量从主内存复制到工作内存,那要顺序执行read和load操作;如果要把变量从工作内存同步回主内存,就要顺序执行store和write操作。Java内存模型要求上述连个操作必须按顺序执行,但不必须连续执行。不允许一个线程丢弃它的最近的assign操作,变量在工作内存中改变了之后必须把变化同步回主内存;不允许一个线程无原因地把数据从线程的工作内存同步回主内存;一个新变量之能在主内存诞生,不允许在工作内存直接使用一个为被初始化(load或assign)的变量,即在对一个变量实施use和store操作之前,必须先执行assign和load操作;一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才被解锁;如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值;如果一个变量事先没有被lock操作锁定,则不允许对他执行unlock操作;也不允许去unlock一个被其他线程锁定住的变量;对一个变量执行unlock操作之前,必须先把此变量同步回主内存。volatile关键字volatile是JVM提供最轻量级的同步机制。当一个变量被定义成volatile后,它具有两个特性:第一,volatile变量对所有线程是立即可见的,对于volatile变量所有的写操作都能立刻反应到其他线程之中;但volatile变量只能保证可见性,不能保证在并发下是安全的。第二,禁止指令重排序优化。Java模型特性原子性:J read、load、assign、use、store和write保证原子性变量操作;lock和unlock通过synchronized语法来加锁,在synchronized块之间的操作也具有原子性;可见性:volatile、synchronized和final实现变量修改值在其他线程可见;synchronized可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中“这条规则获得的;final可见性是”被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把this的引用传递出去,那么在其他线程就能看到final字段的值。“有序性:Java语言提供volatiel和synchronized两个关键字来保证线程之间的有序性;volatile本身禁止指令重排序;synchronized是由“一个变量在同一个时刻只允许一条线程对其进行lock操作“这条规则获得的。2.Java与线程线程是比进程更轻量级的调度执行单位,线程可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进度资源,由可以独立调度。Java线程实现Java在Windows与Linux上都是使用一对一的县城模型来实现,一个Java线程就映射到一条轻量级进程之中。Java线程调度线程调度指系统为线程分配处理器使用权的过程,主要调度方式分为:协同式和抢占式。Java使用的线程调度方式是抢占式调度。协同式,线程的执行时间由线程本身来控制,线程执行完之后主动通知系统切换到另一个线程,它的坏处是线程执行时间不可控制。如果一个线程迟迟不让出CPU执行时间会导致整个系统崩溃。抢占式,每个线程由系统来分配执行时间,线程的切换由操作系统决定。状态装换Java定义了5种线程状态,在任意一个时间点,一个县城智能有且只有有其中的一种状态,即:新建(New):创建后尚未启动的线程;运行(Runable):包括了操作系统线程状态中的Running和Ready,处于此状态的线程可能正在执行,也可能正在等待CPU为它分配执行时间;无限期等待(Waiting):该状态的线程不会被分配CPU执行时间,他们要等待其他线程显式唤醒。以下方式可以让线程陷入Waiting状态:1.没有设置Timeout参数的Object.wat()方法;2.没有设置Timeout参数的Thread.join()方法;3.LockSupport.park()方法。限制等待(Timed Waiting):该状态的线程也不会被分配CPU执行时间,不过无须等待其他线程显示唤醒,在一定时间之后它们会由系统自动唤醒。以下方式可以让线程陷入Timed Waiting状态:1.Thread.sleep()方法;2.设置了Timeout参数的Ojbect.wait()方法;3.设置了Timeout参数的Thread.join()方法;4.LockSupport.parkNanos()方法;5.LockSupport.parkUntil()方法。阻塞(Blocked):线程被阻塞。在程序等待进入同步区域时,线程进入这种状态。结束(Terminated):终止线程的状态。关联:参考书籍:《深入理解Java虚拟机》本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)首先解读Java内存模型(这里区别于JVM的内存模型,堆、栈、工作区)
  Java 内存模型来屏蔽掉各种硬件和操作系统的内存差异,达到跨平台的内存访问效果。JLS(Java语言规范)定义了一个统一的内存管理模型JMM(Java Memory Model)
  Java内存模型规定了所有的变量都存储在主内存中,此处的主内存仅仅是虚拟机内存的一部分,而虚拟机内存也仅仅是计算机物理内存的一部分(为虚拟机进程分配的那一部分)。
  Java内存模型分为主内存,和工作内存。主内存是所有的线程所共享的,工作内存是每个线程自己有一个,不是共享的。
  每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作(读取、赋值),都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者之间的交互关系如下图:
Java内存间交互操作
& & JLS定义了线程对主存的操作指令:lock,unlock,read,load,use,assign,store,write。这些行为是不可分解的原子操作,在使用上相互依赖,read-load从主内存复制变量到当前工作内存,use-assign执行代码改变共享变量值,store-write用工作内存数据刷新主存相关内容。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
为什么需要要多线程 (充分利用CPU)
  举一个栗子,假设现在要10000条数据,总共需要100分钟。如果是单线程的串行操作,需要100分钟。那么如果同时开10个线程,每一个线程运行100条数据,那么只需要10分钟就可以完成所有的操作。(总之是充分利用物理资源CPU)
多线程的三个特性
1、原子性(Atomicity)  原子性是指一个原子操作在cpu中不可以暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。原子操作保证了原子性问题。
  x++(包含三个原子操作)a.将变量x 值取出放在寄存器中 b.将将寄存器中的值+1 c.将寄存器中的值赋值给x
由Java内存模型来直接保证的原子性变量操作包括read、load、use、assign、store和write六个,大致可以认为基础数据类型的访问和读写是具备原子性的。如果应用场景需要一个更大范围的原子性保证,Java内存模型还提供了lock和unlock操作来满足这种需求,尽管虚拟机未把lock与unlock操作直接开放给用户使用,但是却提供了更高层次的字节码指令monitorenter和monitorexit来隐匿地使用这两个操作,这两个字节码指令反映到Java代码中就是同步块---synchronized关键字,因此在synchronized块之间的操作也具备原子性。
2、可见性(Visibility)
  java 内存模型的主内存和工作内存,解决了可见性问题。  volatile赋予了变量可见——禁止编译器对成员变量进行优化,它修饰的成员变量在每次被线程访问时,都强迫从内存中重读该成员变量的值;而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存,这样在任何时刻两个不同线程总是看到某一成员变量的同一个值,这就是保证了可见性。
可见性就是指当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改。无论是普通变量还是volatile变量都是如此,普通变量与volatile变量的区别是volatile的特殊规则保证了新值能立即同步到主内存,以及每使用前立即从内存刷新。因为我们可以说volatile保证了线程操作时变量的可见性,而普通变量则不能保证这一点。
3、有序性(Ordering)  Java内存模型中的程序天然有序性可以总结为一句话:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内表现为串行语义”,后半句是指“指令重排序”现象和“工作内存主主内存同步延迟”现象。
1. 新建状态(New):新创建了一个线程对象。2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
多线程安全
synchronized
  Synchronized关键字保证了数据读写一致和可见性等问题,但是他是一种阻塞的线程控制方法,在关键字使用期间,所有其他线程不能使用此变量,这就引出了一种叫做非阻塞同步的控制线程安全的需求;(同步机制采用了“以时间换空间”的方式)
  Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。 就跟C中的一样 禁止编译器进行优化~~~~
ThreadLocal
  ThreadLocal不是为了解决多线程访问共享变量,而是为每个线程创建一个单独的变量副本,提供了保持对象的方法和避免参数传递的复杂性。
  顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。(ThreadLocal采用了“以空间换时间”的方式)
Long和Double 64Bit的特殊类型
&  对long和double的简单操作之外,volatile并不能提供原子性。所以,就算你将一个变量修饰为volatile,但是对这个变量的操作并不是原子的,在并发环境下,还是不能避免错误的发生!
  Java内存模型lock、unlock、read、load、assign、user、store、write这8个操作都有原子性,但是java内存模型将没有被volatile修饰的64位的数据的读写操作划分为两次32为的操作来进行,这样的话,多线程并发,就会存在线程可能读取到“半个变量”的值,不过,这种情况非常罕见,目前各平台的商用虚拟机几乎都选择把64位的读写作为原子操作来实现规范的。
Java内存大小
-Xms 为jvm启动时分配的内存,比如-Xms200m,表示分配200M
-Xmx 为jvm运行过程中分配的最大内存,比如-Xms500m,表示jvm进程最多只能够占用500M内存
-Xss 为jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M
note : 解释多线程
  1. Java语言作为高级语言支持多线程的操作,主要是为了解决单线程因阻塞而带来的效率问题,同时也充分利用多核CPU的优势。使用多线程也带了问题,线程之间如何通信?线程之间如何和同步?
  2. 线程之间的通信是依靠共享内存和线程方法的调用来实现。在多线程的体系下,Java的内存模型分为主内存和共享内存,通过内存之间的数据交换,依赖多线程的可见性,实现线程之间的通信;线程具有基本状态,主动调用线程的wait、notify方法也可以实现线程之间的通信。
&  &3.线程的同步也是并发线程操作共享变量所带来的问题。多线程允许使用synchronize、volatile、ThreadLocal来保证多线程的安全。synchronize是一个重量级的锁,直接使得线程阻塞,单线程顺利执行,对于更新变量不会有并发操作,很大程度的降低的系统的性能。volatile是一个轻量级的原子锁,对于volatile修饰的变量,每一次的读和写,都必须和主内存交互,他禁止了编译器和处理器的一些重排序优化。
阅读(...) 评论()java并发编程——内存模型 - 简书
java并发编程——内存模型
1. 并发编程基础概念
并发——在中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个上运行,但任一个时刻点上只有一个程序在处理机上运行——源自百度百科
在并发编程中,我们需要处理两个关键问题:线程之间如何通信和线程之间如何同步,后续篇章将围绕这两个问题进行介绍。
线程通信:是指线程之间以何种机制来交换信息,在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。
线程同步:是指程序用于控制不同线程之间操作发生相对顺序的机制。在Java中,可以通过volatile,synchronized, 锁等方式实现同步。
本文主要介绍java的通信机制,刚介绍常见通信机制主要包括以下两种方式:
共享内存:线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。
消息传递:线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。
Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。在java中,所有实例域、静态域和数组元素存储在堆内存中,堆内存在线程之间共享。
Java线程之间的通信由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。
2. JMM内存模型
JMM(Java Memory Model)是JVM规范中定义的一种Java内存模型,它的目的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台上到能达到一致的内存访问效果。Java内存模型的主要定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节。首先简单说明几个常用名称定义:
变量:这里指包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,后者是线程私有的,不会被共享。
主内存:在java中,实例域、静态域和数组元素是线程之间共享的数据,它们存储在主内存中。
工作内存:每条线程都有自己的工作内存,线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。
线程、主内存和工作内存之间交互关系
线程、主内存和工作内存的交互关系如上图所示,和CPU-缓存-内存很类似。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要在主内存来完成。最后注意,为了获得较好的执行性能,Java内存模型并没有限制执行引擎使用处理器的寄存器或者高速缓存来提升指令执行速度,也没有限制编译器对指令进行重排序。也就是说,在java内存模型中,也会存在缓存一致性问题和指令重排序的问题。
3. 内存间交互操作
关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成:
lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
所以变量读写包含以下几个步骤:
变量从主内存复制到工作内存——顺序执行read和load操作
变量从工作内存同步到主内存——顺序执行store和write操作
注意,Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的。除了定义以上8中原子操作,Java内存模型还规定了上述8种基本操作在执行时必须满足一定的操作规则,例如如不允许read和load单独出现(即不允许一个变量从主内存中读取但工作内存不接受),不允许store和write单独出现(即不允许从工作内存中发起了回写单主内存不接受),这里不一一列举,详细网上搜索即可。Java内存模型还定义了volatile型变量的特殊规则(下一节介绍),以上三种规定共同确定了Java中哪些内存访问操作是安全的即:
8种原子操作+操作规则+volatile规定=Java中哪些内存访问操作是安全的
4. volatile型变量规定
当一个变量被定义为volatile后,将具备两种特性:
特性一:保证此变量对所有线程的可见性
特性二:禁止指令重排序优化
4.1 volatile可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。但是,需要注意的是volatile变量只保证可见性,但是java里面的运算并非全部都是原子操作例如++操作,这样同样导致volatile修饰变量java运算不安全。一般不符合以下两条规则的运算场景中,我们需要通过加锁(synchronized或并发包中的锁)保证变量原子性:
运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值(比如++操作不符合依赖当前值)
变量不需要与其他状态变量共同参与不变约束
常见的volatile修饰变量的场景是用来作为开关控制并发:
volatile开关
4.2 禁止指令重排序
重排序:是指“编译器和处理器”为了提高性能,而在程序执行时会对程序进行的重排序。大致可以分为以下三类:
编译器优化指令重排,不改变单线程语义的情况下,重新安排指令执行的顺序。
指令级并行重排序,该优化主要是为了让程序发挥现代处理器的指令级并行执行能力,前提是这些语句不存在数据依赖。
内存系统重排序,主要发生在处理器读写缓冲区,读写过程看起来是无序的,但最终结果是有序的从Java源代码到最终实际执行的指令序列,会经过下面三种重排序:
实际执行指令序列
以上重排序可能会导致多线程中出现内存可见性问题,针对编译器重排序JMM的编译器重排序规则会禁止特定类型的编译器重排序。而对于后两种重排序,JMM的处理器重排序规则会要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel称之为memory fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序(不是所有的处理器重排序都要禁止)。
下面我们看下jvm如何实现volatile禁止指令重排序的:
volatile变量写操作,jvm会向处理器发送一条Lock前缀命令,将变量所在的缓存行系会到系统内存。其他处理器通过嗅探总线上传播的数据检测自己的数据是否过期,如果发现过期会置为无效,再次使用时会从系统内存获取
Lock前缀命令禁止该指令与之前和之后的读和写指令重排序。
最后,关于volatile禁止重排序几点使用说明:
不会对volatile读与volatile读后面的任意内存操作重排序
不会对volatile写与volatile写之前的任意内存操作重排序
CAS同时具有volatile读和写内存的语义,java的CAS使用现代处理器提供的高效级别的原子指令,这些原子指令以原子方式对内存执行读-改-写操作,这是多处理器中实现同步的关键。
5. JMM内存模型总结
总的来说JMM内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性三个特征来建立的。下面就三个特征分别说明:
5.1 原子性
原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。java内存模型的read、load、assign、use、store和write六个操作直接保证原子性,我们可以任务基本数据类型访问读写是具有原子性(特殊说明long double64位操作根据jvm实现有关)。如果场景中需要大范围的原子性保证,java内存模型提供了lock和unlock操作来满足,对应到java代码关键字即是——synchronized。
5.2 可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。除了上面介绍的volatile外,java还提供了两个关键字实现可见性,synchronized和final。
final的可见性:是指被final修饰的字段在构造器中一旦完成,那么在其他线程就可以看见final字段值
synchronized可见性:是指对一个变量执行unlock操作之前,必须先把次变量同步会主内存这条操作规则限制
5.3 有序性
有序性:即程序执行的顺序按照代码的先后顺序执行。java中天然有序性可以总结为一句话:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另外一个线程,所有操作都是无序的。前半句是指“线程内表现为串行语义”,后半句表示“指令重排”和“工作内存与主内存同步延迟”现象。java提供了volatile和synchronized两个关键字来保证线程之间操作的有序性,这里synchronized则是有“同一时刻只允许一条线程对其进行lock操作”这条操作规定获取的,这个规则决定了同一个锁的两个同步块只能串行进入。
最后,可以发现synchronized关键字可以同时解决上述三个问题,当然这个需要付出代价就是性能问题。
《深入理解java虚拟机》——周志明

我要回帖

更多关于 java线程占用内存大小 的文章

 

随机推荐