【拥抱大厂系列】百度面试官问过的 “JVM内存分配与回收策略原理”,我用这篇文章搞定了
点个赞,看一看,好习惯!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾讯等大厂offer。
在前面的一篇文章深入理解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参数分析。
/*** @ClassName Test_01* @Description 参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:+UseSerialGC -XX:SurvivorRatio=8* @Author 欧阳思海* @Date 2019/12/3 16:00* @Version 1.0**/
public class Test_01 {private static final int M = 1024 * 1024;public static void test() {byte[] alloc1, alloc2, alloc3, alloc4;alloc1 = new byte[5 * M];alloc2 = new byte[5 * M];alloc3 = new byte[5 * M];alloc4 = new byte[10 * M];}public static void main(String[] args) {test();}}
输入结果:
分析
- 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的大小看看情况。
/*** @ClassName Test_01* @Description 参数:-Xms40M -Xmx40M -Xmn20M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC* @Author 欧阳思海* @Date 2019/12/3 16:00* @Version 1.0**/
public class Test_01 {private static final int M = 1024 * 1024;public static void test() {byte[] alloc1, alloc2, alloc3, alloc4;
// alloc1 = new byte[5 * M];
// alloc2 = new byte[5 * M];
// alloc3 = new byte[5 * M];alloc4 = new byte[22 * M];}public static void main(String[] args) {test();}}
我们发现分配失败,Java堆溢出,因为超过了最大值。
step2: 下面我们看一个例子:设置-XX:PretenureSizeThreshold=104,857,600
,这个单位是B字节(Byte/bait),所以这里是100M
。
/*** @ClassName Test_01* @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:PretenureSizeThreshold=104,857,600* @Author 欧阳思海* @Date 2019/12/3 16:00* @Version 1.0**/
public class Test_01 {private static final int M = 1024 * 1024;public static void test() {byte[] alloc1, alloc2, alloc3, alloc4;
// alloc1 = new byte[5 * M];
// alloc2 = new byte[5 * M];
// alloc3 = new byte[5 * M];alloc4 = new byte[500 * M];}public static void main(String[] args) {test();}}
发现新生代没有分配,直接在老年代分配。
注意: 参数
PretenureSizeThreshold
只对Serial
和ParNew
两款收集器有效。
2.2 分析条件②
进入老年代规则:这里需要知道虚拟机对每个对象有个对象年龄计数器,如果对象在Eden出生经过第一次Minor GC后任然存活,并且能够被Survivor容纳,将被移动到Survivor空间中,并且年龄设置为1。接下来,对象在Survivor中每次经过一次Minor GC,年龄就增加1,默认当年龄达到15,就会进入到老年代。
晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold
设置。
在下面的实例中,我们设置-XX:MaxTenuringThreshold=1
。
/*** @ClassName Test_01* @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:MaxTenuringThreshold=1* @Author 欧阳思海* @Date 2019/12/3 16:00* @Version 1.0**/
public class Test_01 {private static final int M = 1024 * 1024;public static void test() {byte[] alloc1, alloc2, alloc3, alloc4;alloc1 = new byte[300 * M];alloc2 = new byte[300 * M];alloc3 = new byte[300 * M];alloc4 = new byte[500 * M];}public static void main(String[] args) {test();}}
从结果可以看出,from和to都没有占用内存,而老年代则占用了很多内存。
2.3 分析条件③
条件③是:如果在Survivor空间中相同年龄所有对象的大小的总和大于Survivor空间的一半,年龄大于等于该年龄的对象直接进入到老年代,而不需要等到参数-XX:MaxTenuringThreshold
设置的年龄。
实例分析
/*** @ClassName Test_01* @Description 参数:-Xms2048M -Xmx2048M -Xmn1024M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC* @Author 欧阳思海* @Date 2019/12/3 16:00* @Version 1.0**/
public class Test_01 {private static final int M = 1024 * 1024;public static void test() {byte[] alloc1, alloc2, alloc3, alloc4;alloc1 = new byte[100 * M];alloc2 = new byte[100 * M];//分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。alloc3 = new byte[900 * M];// alloc4 = new byte[500 * M];}public static void main(String[] args) {test();}}
输入结果:
分配alloc3之前,空间不够,所以minor GC,接着分配alloc3=900M大于Survivor空间一半,直接到老年代。从而发现,survivor占用0,而老年代占用900M。
3 总结
这篇文章主要讲解了JVM内存分配与回收策略的原理,回答了下面的这两个问题。
- 我们生成的对象最开始在哪分配?Eden?Survivor?还是老年代呢?
- 进入到老年代需要满足什么条件呢?
最后,再分享我历时三个月总结的 Java 面试 + Java 后端技术学习指南,这是本人这几年及春招的总结,已经拿到了大厂offer,整理成了一本电子书,拿去不谢,目录如下:
现在免费分享大家,在我的公众号 好好学java 回复 Java面试 即可获取。
有收获?希望老铁们来个三连击,给更多的人看到这篇文章
1、老铁们,关注我的原创微信公众号「好好学java」,专注于Java、数据结构和算法、微服务、中间件等技术分享,保证你看完有所收获。
2、给俺点个赞呗,可以让更多的人看到这篇文章,顺便激励下我继续写作,嘻嘻。
点赞是对我最大的鼓励
↓↓↓↓↓↓
【拥抱大厂系列】百度面试官问过的 “JVM内存分配与回收策略原理”,我用这篇文章搞定了相关推荐
- 【拥抱大厂系列】面试官100%会严刑拷打的 CMS 垃圾回收器,下次面试就拿这篇文章怼回去!
点个赞,看一看,好习惯!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾 ...
- 高德面试官问我:JVM内存溢出后服务还能运行吗,我一顿操作行云流水
文章开篇问一个问题吧,一个java程序,如果其中一个线程发生了OOM,那进程中的其他线程还能运行吗? 接下来做实验,看看JVM的六种OOM之后程序还能不能访问. 在这里我用的是一个springboot ...
- 面试官问我:Redis 内存满了怎么办
转载自 想不到!面试官问我:Redis 内存满了怎么办 Redis占用内存大小 Redis的内存淘汰 LRU算法 LRU在Redis中的实现 LFU算法 问题 Redis占用内存大小 我们知道Redi ...
- 实战系列-被面试官问到Feign原理
导语 事情是这样的,昨天参加了某公司二面,被面试官问道了Spring Cloud的RESTFul远程调用.项目上用到的技术就是OpenFeign,面试官可能自己不是太了解,给他解释一番发现自己还有 ...
- 想不到!面试官问我:Redis 内存满了怎么办?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://rrd.me/et29e Redis占用内存大 ...
- 如果面试官问你:Redis 内存满了怎么办?
来自:掘金(作者:千山qianshan) 原文链接: https://juejin.im/post/5d674ac2e51d4557ca7fdd70 Redis占用内存大小 Redis的内存淘汰 LR ...
- 面试官问你Java线程池--怎么样回答才能让面试官知道你真的懂了!
一.引言 不管是Java面试还是Android面试,线程池都是面试官高频考察的点,那我们怎么回答,才能让面试官了解到我们是真的懂Java线程池了呢?这篇文章不涉及到线程池的使用和原理,如果你还不知道怎 ...
- 面试官问:能否模拟实现JS的bind方法(高频考点)
可以点击上方的话题JS基础系列,查看往期文章 写于2018年11月21日,发布在掘金阅读量1.3w+ 前言 这是面试官问系列的第二篇,旨在帮助读者提升JS基础知识,包含new.call.apply.t ...
- 面试官问:能否模拟实现JS的new操作符(高频考点)
可以点击上方的话题JS基础系列,查看往期文章 这篇文章写于2018年11月05日,new模拟实现,Object.create是面试高频考点,之前发布在掘金有近2万人阅读,现在发布到公众号声明原创. 1 ...
最新文章
- php-cgi占用cpu资源过高的解决方法
- the user operation is waiting for building workspace to complete解决办法
- 关于java的对象数组
- 【采用】信贷业务风控逾期指标及风控模型评估指标
- windows结束线程的三种方式
- PyTorch入门v2.pptx
- pytorch中tensorboard使用
- 三十八、练习、Python判断一个信用卡号是否合理
- leetcode474. 一和零(动态规划)
- fiddler设置中文版本_Python3.x+Fiddler 抓取 APP 数据
- Leetcode - 143. Reorder List
- java 取余_JAVA面试解析(有赞)
- Cognos 增加全局类
- hashmap扩容_我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?
- Linux学习入门--make学习总结
- 自定义字体需要css的,CSS 自定义字体
- 驱动人生win7系统如何升级win10一键装机图文教程
- java画图工具_java画图板工具
- Hive详解之内表、外表和分区
- 并发完全知识点目录--yzy
热门文章
- [CareerCup][Google Interview] 找出现次数
- 使用FLVPlayback组件播放fms(fcs)的流式FLV文件
- X1000应用程序打包流程
- AB1601中volatile的使用
- 区块链BaaS云服务(36)欧盟“用户身份认证”ESSIF
- 近世代数--环同态--环的第二同构定理
- 计算机相关专业学习经验总结
- ARMV8/ARMV9指令集概述(翻译)
- SQL注入手工注入常用的语句
- FileBuffer 与 ImageBuffer 互相转换(滴水PE作业)