点个赞,看一看,好习惯!本文 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只对SerialParNew两款收集器有效。

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内存分配与回收策略原理”,我用这篇文章搞定了相关推荐

  1. 【拥抱大厂系列】面试官100%会严刑拷打的 CMS 垃圾回收器,下次面试就拿这篇文章怼回去!

    点个赞,看一看,好习惯!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了3个月总结的一线大厂Java面试总结,本人已拿腾 ...

  2. 高德面试官问我:JVM内存溢出后服务还能运行吗,我一顿操作行云流水

    文章开篇问一个问题吧,一个java程序,如果其中一个线程发生了OOM,那进程中的其他线程还能运行吗? 接下来做实验,看看JVM的六种OOM之后程序还能不能访问. 在这里我用的是一个springboot ...

  3. 面试官问我:Redis 内存满了怎么办

    转载自 想不到!面试官问我:Redis 内存满了怎么办 Redis占用内存大小 Redis的内存淘汰 LRU算法 LRU在Redis中的实现 LFU算法 问题 Redis占用内存大小 我们知道Redi ...

  4. 实战系列-被面试官问到Feign原理

    导语   事情是这样的,昨天参加了某公司二面,被面试官问道了Spring Cloud的RESTFul远程调用.项目上用到的技术就是OpenFeign,面试官可能自己不是太了解,给他解释一番发现自己还有 ...

  5. 想不到!面试官问我:Redis 内存满了怎么办?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://rrd.me/et29e Redis占用内存大 ...

  6. 如果面试官问你:Redis 内存满了怎么办?

    来自:掘金(作者:千山qianshan) 原文链接: https://juejin.im/post/5d674ac2e51d4557ca7fdd70 Redis占用内存大小 Redis的内存淘汰 LR ...

  7. 面试官问你Java线程池--怎么样回答才能让面试官知道你真的懂了!

    一.引言 不管是Java面试还是Android面试,线程池都是面试官高频考察的点,那我们怎么回答,才能让面试官了解到我们是真的懂Java线程池了呢?这篇文章不涉及到线程池的使用和原理,如果你还不知道怎 ...

  8. 面试官问:能否模拟实现JS的bind方法(高频考点)

    可以点击上方的话题JS基础系列,查看往期文章 写于2018年11月21日,发布在掘金阅读量1.3w+ 前言 这是面试官问系列的第二篇,旨在帮助读者提升JS基础知识,包含new.call.apply.t ...

  9. 面试官问:能否模拟实现JS的new操作符(高频考点)

    可以点击上方的话题JS基础系列,查看往期文章 这篇文章写于2018年11月05日,new模拟实现,Object.create是面试高频考点,之前发布在掘金有近2万人阅读,现在发布到公众号声明原创. 1 ...

最新文章

  1. php-cgi占用cpu资源过高的解决方法
  2. the user operation is waiting for building workspace to complete解决办法
  3. 关于java的对象数组
  4. 【采用】信贷业务风控逾期指标及风控模型评估指标
  5. windows结束线程的三种方式
  6. PyTorch入门v2.pptx
  7. pytorch中tensorboard使用
  8. 三十八、练习、Python判断一个信用卡号是否合理
  9. leetcode474. 一和零(动态规划)
  10. fiddler设置中文版本_Python3.x+Fiddler 抓取 APP 数据
  11. Leetcode - 143. Reorder List
  12. java 取余_JAVA面试解析(有赞)
  13. Cognos 增加全局类
  14. hashmap扩容_我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?
  15. Linux学习入门--make学习总结
  16. 自定义字体需要css的,CSS 自定义字体
  17. 驱动人生win7系统如何升级win10一键装机图文教程
  18. java画图工具_java画图板工具
  19. Hive详解之内表、外表和分区
  20. 并发完全知识点目录--yzy

热门文章

  1. [CareerCup][Google Interview] 找出现次数
  2. 使用FLVPlayback组件播放fms(fcs)的流式FLV文件
  3. X1000应用程序打包流程
  4. AB1601中volatile的使用
  5. 区块链BaaS云服务(36)欧盟“用户身份认证”ESSIF
  6. 近世代数--环同态--环的第二同构定理
  7. 计算机相关专业学习经验总结
  8. ARMV8/ARMV9指令集概述(翻译)
  9. SQL注入手工注入常用的语句
  10. FileBuffer 与 ImageBuffer 互相转换(滴水PE作业)