JVM的内存分配垃圾回收策略
之前看过《深入了解Java虚拟机》感觉容易忘,今天写一篇博客加深一下印象。
JVM的内存分配和垃圾回收(GC)主要发生在Java堆中。而Java堆根据对象的存活时间可以分为新生代和老年代,而新生代又细分为Eden区、From Survivor区、To Survivor区,这是由于新生代中的垃圾回收算法基本都是复制算法。
1.对象优先在Eden区中分配
当Eden区没有足够空间进行分配时,虚拟机会发起一次新生代GC(Minor GC)。因为Java对象大多数都具有朝生夕灭的特性,所以Minor GC非常频繁,回收速度也比较快。
我们可以通过参数-XX:+PrintGCDetails来查看GC日志。下面举个实际例子来看看是不是优先在Eden区中分配内存。
-verbose:gc
-Xms20M
-Xmx20M
-Xmn10M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
设置的参数
package MinorGC; public class MinorGC {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
byte[] allocation1,allocation2,allocation3;
allocation1 = new byte[1*_1MB];
allocation2 = new byte[2*_1MB];
allocation3 = new byte[2*_1MB];
}
}
可以看到控制台输出
新生代的内存使用情况:total 9216K, used 7482K
老年代的内存使用情况:total 10240K, used 0K
这样可以看出对象优先在Eden区中分配。
2.大对象直接进入老年代
大对象是指需要大量连续内存空间的Java对象,例如很长的字符串和数组,上面代码中的byte[]数组就是大数组。经常出现大对象容易导致内存中还有不少内存就要提前GC来获取连续的空间来安放它们。
虚拟机中可以设置参数-XX:PretenureSizeThreshold参数来让大于这个设置值的对象直接在老年代分配,这样的目的是避免在Eden区和两个Survivor区之间进行大量的内存复制。
下面测试一下,这时候要注意一下我用的是Java8,默认的垃圾回收器是Parallel Scavenge(新生代)+Parallel Old(老年代),而Parallel Scavenge不认识参数
-XX:PretenureSizeThreshold(Parallel Scavenge不需要设置),所以要先加上-XX:+UseSerialGC来将虚拟机的垃圾回收器设置成Serial / Serial Old回收器。
-verbose:gc
-Xms20M
-Xmx20M
-Xmn10M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=3145728
-XX:+UseSerialGC
设置的参数
package MinorGC; public class MinorGC {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
byte[] allocation4;
// allocation1 = new byte[_1MB];
// allocation2 = new byte[2*_1MB];
// allocation3 = new byte[2*_1MB];
allocation4 = new byte[4*_1MB];
}
}
可以发现4MB的对象直接放到老年代中了
3.长期存活的对象将进入老年区
虚拟机给每个对象定义一个对象年龄计数器。如果对象在Eden区出生并经过第一次Minor GC后仍然存活,并能被Survivor容纳,将被移动到Survivor的空间中,并且对象年龄设为1对象在Survivor区每“熬过”一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认15岁),就将被晋升到老年代中。JVM中可以通过-XX:MaxTenuringThreshold设置。
下面我们测试一下
-verbose:gc
-Xms20M
-Xmx20M
-Xmn10M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=1
-XX:+PrintTenuringDistribution
-XX:+UseSerialGC
设置的参数
package MinorGC; public class MinorGC {
private static final int _1MB = 1024*1024;
@SuppressWarnings("unused")
public static void main(String[] args) {
byte[] allocation1, allocation2, allocation3;
allocation1 = new byte[_1MB / 4];
// 什么时候进入老年代取决于XX:MaxTenuringThreshold设置
allocation2 = new byte[4 * _1MB];
allocation3 = new byte[4 * _1MB];
allocation3 = null;
allocation3 = new byte[4 * _1MB];
}
}
该方法中allocation1对象需要256KB内存,Survivor区可以容纳,所以讲它放到Survivor区中并让它年龄加1,。在第二次GC发生后它就进入老年代了,而Survivor区刚好被清除干净。
4.动态对象年龄判定
为了更好地适应不同程序的内存状况,虚拟机并不是永远要求对象的年龄必须达到了MaxTenuringThreshold才晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半, 年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
测试
配置和前面的一样,只是MaxTenuringThreshold=15
package MinorGC; public class MinorGC {
private static final int _1MB = 1024*1024;
@SuppressWarnings("unused")
public static void main(String[] args) {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[_1MB / 4];
// allocation1+allocation2大于survivo空间一半
allocation2 = new byte[_1MB / 4];
allocation3 = new byte[4 * _1MB];
allocation4 = new byte[4 * _1MB];
allocation4 = null;
allocation4 = new byte[4 * _1MB];
}
}
发现运行结果中Survivor的空间占用仍然为0%,而老年代比预期增加了6%,也就是说,allocation1、allocation2对象都直接进入了老年代,而没有等到15岁的临界年龄。因为这两个对象加起来已经到达了
512KB,并且它们是同年的,满足同年对象达到Survivor空间的一半规则。
在注释掉allocation2后,发现只有一个256KB进入了老年代。
5.空间分配担保
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。
新生代使用复制收集算法,但为了内存利用率,只使用其中一个Survivor空间来作为轮换备份,因此当出现大量对象在Minor GC后仍然存活的情况(最极端的情况就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。与生活中的贷款担保类似,老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会活下来在实际完成内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC来让老年代腾出更多空间。
取平均值进行比较其实仍然是一种动态概率的手段,也就是说,如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败(Handle Promotion Failure)。如果出现了HandlePromotionFailure失败,那就只好在失败后重新发起一次Full GC。虽然担保失败时绕的圈子是最大的,但大部分情况下都还是会将HandlePromotionFailure开关打开,避免Full GC过于频繁。
补充一下参数有哪些:
1、-Xmx –Xms:指定最大堆和最小堆
2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio:
- -Xmn:设置新生代大小
- -XX:NewRatio:新生代(eden+2*s)和老年代(不包含永久区)的比值
例如:4,表示新生代:老年代=1:4,即新生代占整个堆的1/5
- -XX:SurvivorRatio(幸存代)设置两个Survivor区和eden的比值
例如:8,表示两个Survivor:eden=2:8,即一个Survivor占年轻代的1/10
参考自:深入理解JVM
https://www.jianshu.com/p/fa3569127416
JVM的内存分配垃圾回收策略的更多相关文章
- jvm之内存分配与回收策略
1.java堆中各代分布 (1)Young:主要是用来存放新生的对象. (2)Old:主要存放应用程序中生命周期长的内存对象. (3)Permanent:是指内存的永久保存区域,主要存放Class和M ...
- JVM的内存分配和回收策略
对象的Class加载 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化过.如果没有,那必须先执行相应 ...
- JVM:内存分配与回收策略
Java技术体系中所提倡的自动内存管理最终可以归结为自动化的解决了两个问题:给对象分配内存以及回收分配给对象的内存. 对象的内存分配,往大方向讲,就是在堆上分配(但也可能经过JIT编译后被拆散为标量类 ...
- Java虚拟机垃圾回收:内存分配与回收策略 方法区垃圾回收 以及 JVM垃圾回收的调优方法
在<Java对象在Java虚拟机中的创建过程>了解到对象创建的内存分配,在<Java内存区域 JVM运行时数据区>中了解到各数据区有些什么特点.以及相关参数的调整,在<J ...
- JVM垃圾回收器、内存分配与回收策略
新生代垃圾收集器 1. Serial收集器 serial收集器即串行收集器,是一个单线程收集器. 串行收集器在进行垃圾回收时只使用一个CPU或一条收集线程去完成垃圾回收工作,并且会暂停其他的工作线程( ...
- JVM学习十 -(复习)内存分配与回收策略
内存分配与回收策略 对象的内存分配,就是在堆上分配(也可能经过 JIT 编译后被拆散为标量类型并间接在栈上分配),对象主要分配在新生代的 Eden 区上,少数情况下可能直接分配在老年代,分配规则不固定 ...
- Java虚拟机内存分配与回收策略
内存分配与回收策略 Minor GC 和 Full GC Minor GC:发生在新生代上,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行, 执行的速度一般也会比较快. Full GC ...
- jvm内存分配和回收策略
在上一篇中,已经介绍了内存结构是什么样的. 这篇来介绍一下 内存是怎么分配的,和怎么回收的.(基本取自<深入理解Java虚拟机>一书) java技术体系中所提倡的自动内存管理最终可以归结为 ...
- JVM 内存分配和回收策略
对象的内存分配,主要是在java堆上分配(有可能经过JIT编译后被拆为标量类型并间接地在栈上分配),如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配.少数情况下也是直接分配到老年代,分配规则不 ...
随机推荐
- uva12558埃及分数
1,看这全英文的题目就怪蛋疼的. 2,这输入也是奇奇怪怪的的.3,想要好好做题,理解做题,就得好好看题自己要理解吸收消化.单纯看别人的话,说实话并没有什么用处. 一,看题. 1,首先,枚举的分数肯定不 ...
- LeetCode 732. My Calendar III
原题链接在这里:https://leetcode.com/problems/my-calendar-iii/ 题目: Implement a MyCalendarThree class to stor ...
- tensorflow 2.0 学习(四)
这次的mnist学习加入了测试集,看看学习的准确率,代码如下 # encoding: utf-8 import tensorflow as tf import matplotlib.pyplot as ...
- js 符号转换 html代码
S转换HTML转义符 //去掉html标签 function removeHtmlTab(tab) { return tab.replace(/<[^<>]+?>/g,''); ...
- mysql 查询账户
查询 mysql 的存在的账户 >select user,host,password from mysql.user; # 可以查询涉及到user. host 链接权限.密码加密文件.
- 【POJ2251】Dungeon Master
本题传送门 本题知识点:宽度优先搜索 题意简单.在一个L层高的楼里,去走迷宫,就是问从S走到E的最短路径.每走一格每上或者下一层都算1步. 一开始以为这个"立体迷宫"有点吓到我(题 ...
- pyqt5设置背景图片出现问题
在使用pyqt5时,用qtdesign设置好背景图片,如何设置自行百度,预览没问题,用ptuic5转换为代码却发现显示不了: 首先:我在qtdesign中导入的是pic.qrc,但是转换的代码最后一句 ...
- 正则表达式在线分析 regex online analyzer
https://regexper.com/#%2F%5B0-9%5D%5Cs%5B0-9%5D%2F https://regexper.com/ http://regexone.com/lesson/ ...
- Android 加密之文件级加密(CE/DE)
https://blog.csdn.net/myfriend0/article/details/77094890/ Android加密之文件级加密
- WebGL学习笔记(七):输入和动画
目前为止,我们绘制出来的3D物体都是静止的,接下来我们需要让桌面上的小盒子可以根据我们按键(上下键)前进后退: 输入方面,监听按键和鼠标消息直接在document上添加对应的监听就行了: 动画这块,我 ...