深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理,从此告别 JVM 内存分配文盲
点击上方 好好学java ,选择 星标 公众号
重磅资讯、干货,第一时间送达
今日推荐:打卡活动第二期来啦,100% 能获得奖品个人原创+1博客:点击前往,查看更多
虚拟机系列文章
深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析
深入理解 Java 虚拟机(第二弹) - 常用 vm 参数分析
深入理解 Java 虚拟机-如何利用 VisualVM 对高并发项目进行性能分析
在前面的一篇文章深入理解Java虚拟机-如何利用VisualVM进行性能分析中讲到了一些关于JVM调优的知识,但是,其实,还是有一些问题没有非常清楚的可以回答的,这里先给出几个问题,然后,我们再展开这篇文章需要讲解的知识。
我们生成的对象最开始在哪分配?Eden?Survivor?还是老年代呢?
进入到老年代需要满足什么条件呢?
接下来,我们就带着这两个问题展开全文。
1 对象优先在哪分配
其实,通过前面几篇文章的讲解,这个问题其实已经见怪不怪了,在大多数的情况下,对象都是在新生代Eden区分配的,在前面的文章我们提到,在Eden区中如果内存不够分配的话,就会进行一次Minor GC
。同时,我们还知道年轻代中默认下Eden:Survivor0:Survivor2 = 8:1:1
,同时,还能通过参数-XX:SurvivorRatio
来设置这个比例(关于这些参数的分析都可以查看这篇文章:深入理解Java虚拟机-常用vm参数分析)。
下面我们通过一个例子来分析是不是这样的。
1.1 实例
给定JVM参数:-Xms40M -Xmx40M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=4
前面三个参数设置Java堆的大小为40M,新生代为10M,紧跟着后面两个是用于输入GC信息。更多参数可以查看这篇文章:深入理解Java虚拟机-常用vm参数分析。
1/**2 * @ClassName Test_013 * @Description 参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:+UseSerialGC -XX:SurvivorRatio=84 * @Author 欧阳思海5 * @Date 2019/12/3 16:006 * @Version 1.07 **/8public class Test_01 {9
10 private static final int M = 1024 * 1024;
11
12 public static void test() {
13 byte[] alloc1, alloc2, alloc3, alloc4;
14 alloc1 = new byte[5 * M];
15 alloc2 = new byte[5 * M];
16 alloc3 = new byte[5 * M];
17 alloc4 = new byte[10 * M];
18
19 }
20
21 public static void main(String[] args) {
22 test();
23 }
24
25}
输入结果:
分析
eden:from:to=8:1:1,这个因为前面设置了参数
-XX:SurvivorRatio=8
。新生代分配了20M的内存,所以前面三个
byte
数组可以分配,但是,分配第四个的时候,空间不够,所以,需要进行一次Minor GC
,GC之后,新生代从12534K
变为598K
。前面在新生代分配的内存
Minor GC
之后,进入到了Survivor
,但是,Survivor不够分配,所以进入到了老年代
,老年代已用内存达到了50%
。
1.2 回答问题
所以,经过上面的例子我们发现,对象一般优先在新生代分配的,如果新生代内存不够,就进行Minor GC回收内存。
2 进入到老年代需要满足什么条件
先给出答案,分为几点。
条件①:大对象直接进入到老年代
条件②:长期存活的对象可以进入到老年代
条件③:如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入到老年代
2.1 分析条件①
哪些属于大对象呢?
一般来说大对象指的是很长的字符串及数组,或者静态对象。
那么需要满足多大才是大对象呢?
这个虚拟机提供了一个参数-XX:PretenureSizeThreshold=n
,只需要大于这个参数所设置的值,就可以直接进入到老年代。
step1: 解决了这两个问题,首先,我们不设置上面的参数的例子,将对象的内存大于Eden的大小看看情况。
1/**2 * @ClassName Test_013 * @Description 参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC4 * @Author 欧阳思海5 * @Date 2019/12/3 16:006 * @Version 1.07 **/8public class Test_01 {9
10 private static final int M = 1024 * 1024;
11
12 public static void test() {
13 byte[] alloc1, alloc2, alloc3, alloc4;
14// alloc1 = new byte[5 * M];
15// alloc2 = new byte[5 * M];
16// alloc3 = new byte[5 * M];
17 alloc4 = new byte[22 * M];
18
19 }
20
21 public static void main(String[] args) {
22 test();
23 }
24
25}
我们发现分配失败,Java堆溢出,因为超过了最大值。
step2: 下面我们看一个例子:设置-XX:PretenureSizeThreshold=104,857,600
,这个单位是B字节(Byte/bait),所以这里是100M
。
1/**2 * @ClassName Test_013 * @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:PretenureSizeThreshold=104,857,6004 * @Author 欧阳思海5 * @Date 2019/12/3 16:006 * @Version 1.07 **/8public class Test_01 {9
10 private static final int M = 1024 * 1024;
11
12 public static void test() {
13 byte[] alloc1, alloc2, alloc3, alloc4;
14// alloc1 = new byte[5 * M];
15// alloc2 = new byte[5 * M];
16// alloc3 = new byte[5 * M];
17 alloc4 = new byte[500 * M];
18
19 }
20
21 public static void main(String[] args) {
22 test();
23 }
24
25}
发现新生代没有分配,直接在老年代分配。
注意: 参数
PretenureSizeThreshold
只对Serial
和ParNew
两款收集器有效。
2.2 分析条件②
进入老年代规则:这里需要知道虚拟机对每个对象有个对象年龄计数器,如果对象在Eden出生经过第一次Minor GC后任然存活,并且能够被Survivor容纳,将被移动到Survivor空间中,并且年龄设置为1。接下来,对象在Survivor中每次经过一次Minor GC,年龄就增加1,默认当年龄达到15,就会进入到老年代。
晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold
设置。
在下面的实例中,我们设置-XX:MaxTenuringThreshold=1
。
1/**2 * @ClassName Test_013 * @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:MaxTenuringThreshold=14 * @Author 欧阳思海5 * @Date 2019/12/3 16:006 * @Version 1.07 **/8public class Test_01 {9
10 private static final int M = 1024 * 1024;
11
12 public static void test() {
13 byte[] alloc1, alloc2, alloc3, alloc4;
14 alloc1 = new byte[300 * M];
15 alloc2 = new byte[300 * M];
16 alloc3 = new byte[300 * M];
17 alloc4 = new byte[500 * M];
18
19 }
20
21 public static void main(String[] args) {
22 test();
23 }
24
25}
从结果可以看出,from和to都没有占用内存,而老年代则占用了很多内存。
2.3 分析条件③
条件③是:如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入到老年代,而不需要等到参数-XX:MaxTenuringThreshold
设置的年龄。
实例分析
1/**2 * @ClassName Test_013 * @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC4 * @Author 欧阳思海5 * @Date 2019/12/3 16:006 * @Version 1.07 **/8public class Test_01 {9
10 private static final int M = 1024 * 1024;
11
12 public static void test() {
13 byte[] alloc1, alloc2, alloc3, alloc4;
14 alloc1 = new byte[100 * M];
15 alloc2 = new byte[100 * M];
16 //分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。
17 alloc3 = new byte[900 * M];
18
19// alloc4 = new byte[500 * M];
20
21 }
22
23 public static void main(String[] args) {
24 test();
25 }
26
27}
输入结果:
分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。从而发现,survivor占用0,而老年代占用900M。
3 总结
这篇文章主要讲解了JVM内存分配与回收策略的原理,回答了下面的这两个问题。
我们生成的对象最开始在哪分配?Eden?Survivor?还是老年代呢?
进入到老年代需要满足什么条件呢?
更多Java技术文章,尽在【好好学java】网站。网址:www.java1000.com 搜索 好好学java 或 阅读原文 可达!
深入理解Java虚拟机(第三弹)- JVM 内存分配与回收策略原理,从此告别 JVM 内存分配文盲相关推荐
- 深入理解java虚拟机gc_jvm GC收集器与内存分配(深入理解java虚拟机第三章)
jvm GC收集器与内存分配(深入理解java虚拟机第三章) 本篇是<深入理解java虚拟机第三章>的笔记记录. 一 为什么要关注GC和内存分配? 需要排查各种内存溢出.内存泄漏问题时,或 ...
- 深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析
来自:好好学java 这篇文章主要介绍Java内存区域,也是作为Java虚拟机的一些最基本的知识,理解了这些知识之后,才能更好的进行Jvm调优或者更加深入的学习,本来这些知识是晦涩难懂的,所以希望能够 ...
- 深入理解 Java 虚拟机(第二弹) - 常用 vm 参数分析
来自:好好学java 话不多说,今天就分析一下一些常用的Java虚拟机的参数设置,以及如何更好的使用! 1 JVM参数简介 首先想说的是其实这些参数我们并不是陌生的,在平时的开发和使用中经常都会遇到, ...
- 深入理解Java虚拟机(三)之详拆运行时数据区的各个结构----方法区(元空间)
目录 栈.堆.方法区的交互关系 方法区的理解 设置方法区的大小和OOM 方法区的内部结构 存放内容 类型信息 域信息 方法(Method)信息. 静态变量 常量(static final) 运行时常量 ...
- 【拥抱大厂系列】百度面试官问过的 “JVM内存分配与回收策略原理”,我用这篇文章搞定了
点个赞,看一看,好习惯!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾 ...
- 深入理解Java虚拟机(三)——对象已死吗
垃圾收集器(对象回收条件,四大引用,对象的自我拯救) 概述 前面我l们了解到了Java内存运行时区域的各个部分,其中程序计数器,虚拟机栈,本地方法栈这三个区域随线程而生,随线程而灭.因此这几个区域的内 ...
- JVM的内存分配与回收策略实战——对象优先在Eden分配
本文代码在测试时使用的垃圾收集器组合是Serial/Serial Old. 这里还需要介绍一些JVM的基本参数,本文中进行如下设置: -verbose gc 打印垃圾收集信息 -Xms20M 堆容量设 ...
- 深入理解 Java 虚拟机 - 你了解 GC 算法原理吗
来自:好好学Java 虚拟机系列文章 深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析 深入理解 Java 虚拟机(第二弹) - 常用 vm 参数分析 深入理解 Java 虚拟机- ...
- 深入理解Java虚拟机-如何利用 JDK 自带的命令行工具监控上百万的高并发的虚拟机性能...
虚拟机系列文章 深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析 深入理解 Java 虚拟机(第二弹) - 常用 vm 参数分析 深入理解 Java 虚拟机-如何利用 Visual ...
最新文章
- 微服务化后,这几点一定要注意
- 大年初七,发paper、学Python...分享一下你的学习计划吧~
- python3 异步 asyncio aiohttp aiohttp-requests aiofiles 使用
- 【机器学习入门到精通系列】机器学习系统设计(Precision Recall)
- 分区格式化/挂载数据盘
- waf可以查看post请求吗_WAFNinja:一款绕过WAF的渗透工具
- Windows监听进程是否退出C++
- VS启动调试速度异常的缓慢问题
- DropDownList 递归绑定分子公司信息
- Linux下连接mongoDB出现no reachable servers以及mongoDB创建用户
- ecshop系统前后台出现的几个错误修正升级到PHP5.6后
- android计时器和倒计时
- 用Python向MongoDB中插入大csv文件
- 租衣APP开发前景分析
- Linux USB 驱动开发(三)—— 编写USB 驱动程序
- C语言视频教程-谭浩强版-小甲鱼主讲—P18
- Redis主从模式下从库过期的key仍然能够被读到的解决方案
- HDU2549 壮志难酬【水题】
- 多核与多个CPU啥区别?
- unity聚光灯_聚光灯团队最佳实践:碰撞性能优化
热门文章
- 使用 Linux 系统调用的内核命令图解
- python离线包安装_python 通过pip freeze、dowload打离线包及自动安装的过程详解(适用于保密的离线环境...
- 近世代数--整环--高斯整环
- 攻防世界第四题Reverse re1
- 密码(图解密码技术)_第二章_历史上的密码
- CSAPP第4章家庭作业参考答案
- ida pro 7.5 idapython学习
- 详解虚函数的实现过程之初探虚表(1)
- 2020-10-26(对Dex文件的理解)
- Windows保护模式学习笔记(十三)—— PWTPCD