JVM-内存分配与回收

一、内存分布

Java虚拟机所管理的内存将会包括以下几个运行时数据区域,如下图所示:

113350_6NcW_3644171.png

本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与

虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

(个人理解:虚拟机栈就是通常所说的栈,本地方法栈服务于native方法)

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

直接内存(Direct Memory)

直接内存并不是JVM管理的内存,但这部分内存经常被使用,也可能导致OutOfMemoryError异常。

JDK中有一种基于通道(Channel)和缓冲区(Buffer)的I/O方式,将由C语言实现的native函数库直接分配堆外内存,用存储在JVM堆中的DirectByteBuffer来引用。由于直接内存收到本机器内存的限制,所以也可能出现OutOfMemoryError的异常。

二、需要回收的区域和对象

回收区域:堆和方法区

如何判断一个对象已死(即可回收对象),一般有引用计数算法和可达性分析算法

1、引用计数算法:每个对象都有一个引用计数器,每当有一个地方引用它时计数器就加1,引用失效时计数器就减1,当计数器为0时,该对象就可以回收。缺点是循环引用对象不能被回收。

2、可达性分析算法:一系列“GC ROOTs”的对象作为起点,当某个对象与GC ROOTS不可达时,该对象即被判断为可回收对象。可以作为GC ROOTs对象的有以下几种:

113503_MIJY_3644171.png

113521_pIuV_3644171.png

3、扩充的引用

引用分为四种:(不会被回收的,可回收也可不回收,肯定会被回收、不需要理会的)

  a> 强引用(Strong Reference).就是为刚被new出来的对象所加的引用,它的特点就是,永远不会被回收。

  b> 软引用(Soft Reference).声明为软引用的类,是可被回收的对象,如果JVM内存并不紧张,这类对象可以不被回收,如果内存紧张,则会被回收。此处有一个问题,既然被引用为软引用的对象可以回收,为什么不去回收呢?其实我们知道,Java中是存在缓存机制的,就拿字面量缓存来说,有些时候,缓存的对象就是当前可有可无的,只是留在内存中如果还有需要,则不需要重新分配内存即可使用,因此,这些对象即可被引用为软引用,方便使用,提高程序性能。

 c> 弱引用(Weak Reference).弱引用的对象就是一定需要进行垃圾回收的,不管内存是否紧张,当进行GC时,标记为弱引用的对象一定会被清理回收。

 d> 虚引用(Phantom Reference).虚引用弱的可以忽略不计,JVM完全不会在乎虚引用,其唯一作用对象被回收时会收到一些通知。

4、对象回收处理过程

对象被回收前需要进行两次标记过程。第一次是对象与GC Roots间不可达,即被第一次标记并且筛选该对象是否有必要执行finalize()方法,将筛选的对象放到一个执行队列中,没有必要执行finalize方法的对象会被回收;第二次仅对队列中的对象进行标记,如果在执行finalize方法中将对象与GC ROOTS关联上,则该对象将不会被回收,否则该对象将被回收。

三、内存分配

Java内存分配和回收的机制概括的说,就是:分代分配,分代回收。对象将根据存活的时间被分为:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)

113554_pNWC_3644171.png

年轻代(Young Generation):对象被创建时,内存的分配首先发生在年轻代(大对象可以直接 被创建在年老代),大部分的对象在创建后很快就不再使用,因此很快变得不可达,于是被年轻代的GC机制清理掉(IBM的研究表明,98%的对象都是很快消 亡的),这个GC机制被称为Minor GC或叫Young GC。注意,Minor GC并不代表年轻代内存不足,它事实上只表示在Eden区上的GC。

年轻代上的内存分配是这样的,年轻代可以分为3个区域:Eden区(伊甸园,亚当和夏娃偷吃禁果生娃娃的地方,用来表示内存首次分配的区域,再 贴切不过)和两个存活区(Survivor 0 、Survivor 1)。内存分配过程为:

113632_JkLN_3644171.png

  1. 绝大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡。Eden区是连续的内存空间,因此在其上分配内存极快;
  2. 当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的);
  3. 此后,每次Eden区满了,就执行一次Minor GC,并将剩余的对象都添加到Survivor0;
  4. 当Survivor0也满的时候,将其中仍然活着的对象直接复制到Survivor1,以后Eden区执行Minor GC后,就将剩余的对象添加Survivor1(此时,Survivor0是空白的)。
  5. 当两个存活区切换了几次(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代)之后,仍然存活的对象(其实只有一小部分,比如,我们自己定义的对象),将被复制到老年代。

 

年老代(Old Generation):对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次 Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时, 将执行Major GC,也叫 Full GC。

 

四、垃圾收集算法

1、标记-清除算法

分两个阶段,标记阶段和清除阶段。首选标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。

2、复制算法

将内存分为大小相等的两块,每次只使用其中一块。当一块内存用完了,就将活着的对象复制到另一块上面,最后一次性清除已使用过的内存空间。(一般新生代采用这种算法)

3、标记-整理算法

也是分为两个阶段。第一个阶段是标记,第二个阶段是将所有存活对象移动到一端,然后直接清理掉边界以外的内存。

4、分代收集算法

根据对象存活时间不同将内存分为几部分。一般将堆分为新生代和年老代,根据年代的特点采用最适合的收集算法。例如,新生代一般采用复制算法,年老代采用标记-清除或者标记-整理算法。

五、JVM常用内存参数设置

a: -Xmx<n> 

指定 jvm 的最大 heap 大小 , 如 :-Xmx=2g 

 

b: -Xms<n> 

指定 jvm 的最小 heap 大小 , 如 :-Xms=2g , 高并发应用, 建议和-Xmx一样, 防止因为内存收缩/突然增大带来的性能影响。 

 

c: -Xmn<n> 

指定 jvm 中 New Generation 的大小 , 如 :-Xmn256m。 这个参数很影响性能, 如果你的程序需要比较多的临时内存,建议设置到512M, 如果用的少, 尽量降低这个数值, 一般来说128/256足以使用了。 

 

d: -XX:PermSize=<n> 

指定 jvm 中 Perm Generation 的最小值 , 如 :-XX:PermSize=32m。 这个参数需要看你的实际情况,。可以通过jmap 命令看看到底需要多少。 

 

e: -XX:MaxPermSize=<n> 

指定 Perm Generation 的最大值 , 如 :-XX:MaxPermSize=64m 

 

f: -Xss<n> 

指定线程桟大小 , 如 :-Xss128k, 一般来说,webx框架下的应用需要256K。 如果你的程序有大规模的递归行为,请考虑设置到512K/1M。 这个需要全面的测试才能知道。 不过,256K已经很大了。 这个参数对性能的影响比较大的。 

 

g: -XX:NewRatio=<n> 

指定 jvm 中 Old Generation heap size 与 New Generation 的比例 , 在使用 CMS GC 的情况下此参数失效 , 如 :-XX:NewRatio=2 

 

h: -XX:SurvivorRatio=<n> 

指 定 New Generation 中 Eden Space 与一个 Survivor Space 的 heap size 比例 ,-XX:SurvivorRatio=8, 那么在总共 New Generation 为 10m 的情况下 ,Eden Space 为 8m 

 

i: -XX:MinHeapFreeRatio=<n> 

指定 jvm heap 在使用率小于 n 的情况下 ,heap 进行收缩 ,Xmx==Xms 的情况下无效 , 如 :-XX:MinHeapFreeRatio=30 

 

j: -XX:MaxHeapFreeRatio=<n> 

指定 jvm heap 在使用率大于 n 的情况下 ,heap 进行扩张 ,Xmx==Xms 的情况下无效 , 如 :-XX:MaxHeapFreeRatio=70 

 

k: -XX:LargePageSizeInBytes=<n> 

指定 Java heap 的分页页面大小 , 如 :-XX:LargePageSizeInBytes=128m 

 

六、内存分配与回收策略

对象优先在Eden分配;

大对象直接进入老年代;(例如:长字符串和数组)

长期存活的对象将进入老年代;(Survivor空间中经过一定次数minor GC仍旧存活的对象)

动态对象年龄判定;(Survivor空间中对象不一定非等到一定年龄才能移动到年老代,也可以通过相同年龄对象总大小大于Survivor空间的一半,该年龄及以上大小的对象都被移动到年老代)

空间分配担保;(根据年老代上的最大可用连续空间是否大于整个新生代对象的空间,如果大于可以确保是安全的,可以直接minor GC;如果不是根据情况选择minor GC还是full GC)

 

转载于:https://my.oschina.net/u/3644171/blog/1553791

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值