当前位置: 首页 > news >正文

java面试-深入理解JVM(四)——对象内存的分配策略

Java所承诺的自动内存管理主要是针对对象内存的回收和对象内存的分配。

在Java虚拟机的五块内存空间中,程序计数器、Java虚拟机栈、本地方法栈内存的分配和回收都具有确定性,一般在编译阶段就能确定需要分配的内存大小,并且由于都是线程私有,因此它们的内存空间都随着线程的创建而创建,线程的结束而回收。也就是这三个区域的内存分配和回收都具有确定性,垃圾回收器不需要在这里花费太大的精力。

而Java虚拟机中的方法区因为是用来存储类信息、常量、静态变量,这些数据的变动性较小,因此不是Java内存管理重点需要关注的区域。

而对于堆,所有线程共享,所有的对象都需要在堆中创建和回收。虽然每个对象的大小在类加载的时候就能确定,但对象的数量只有在程序运行期间才能确定,因此堆中内存的分配具有较大的不确定性。此外,对象的生命周期长短不一,因此需要针对不同生命周期的对象采用不同的内存回收算法,增加了内存回收的复杂性。

综上所述:Java自动内存管理最核心的功能是堆内存中对象的分配与回收。 

对象优先在Eden区中分配

目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代。

在新生代中为了防止内存碎片问题,因此垃圾收集器一般都选用“复制”算法。因此,堆内存的新生代被进一步分为:Eden区+Survior1区+Survior2区。

每次创建对象时,首先会在Eden区中分配。 
若Eden区已满,则在Survior1区中分配。 
若Eden区+Survior1区剩余内存太少,导致对象无法放入该区域时,就会启用“分配担保”,将当前Eden区+Survior1区中的对象转移到老年代中,然后再将新对象存入Eden区。 

大对象直接进入老年代

所谓“大对象”就是指一个占用大量连续存储空间的对象,如数组。

当发现一个大对象在Eden区+Survior1区中存不下的时候就需要分配担保机制把当前Eden区+Survior1区的所有对象都复制到老年代中去。 
我们知道,一个大对象能够存入Eden区+Survior1区的概率比较小,发生分配担保的概率比较大,而分配担保需要涉及到大量的复制,就会造成效率低下。 
因此,对于大对象我们直接把他放到老年代中去,从而就能避免大量的复制操作。 
那么,什么样的对象才是“大对象”呢?

通过-XX:PretrnureSizeThreshold参数设置大对象

该参数用于设置大小超过该参数的对象被认为是“大对象”,直接进入老年代。 
注意:该参数只对Serial和ParNew收集器有效。 

生命周期较长的对象进入老年代

老年代用于存储生命周期较长的对象,那么我们如何判断一个对象的年龄呢?

新生代中的每个对象都有一个年龄计数器,当新生代发生一次MinorGC后,存活下来的对象的年龄就加一,当年龄超过一定值时,就将超过该值的所有对象转移到老年代中去。

使用-XXMaxTenuringThreshold设置新生代的最大年龄

设置该参数后,只要超过该参数的新生代对象都会被转移到老年代中去。 

相同年龄的对象内存超过Survior内存一半的对象进入老年代

如果当前新生代的Survior中,年龄相同的对象的内存空间总和超过了Survior内存空间的一半,那么所有年龄相同的对象和超过该年龄的对象都被转移到老年代中去。无需等到对象的年龄超过MaxTenuringThreshold才被转移到老年代中去。 

“分配担保”策略详解

当垃圾收集器准备要在新生代发起一次MinorGC时,首先会检查“老年代中最大的连续空闲区域的大小 是否大于 新生代中所有对象的大小?”,也就是老年代中目前能够将新生代中所有对象全部装下?

若老年代能够装下新生代中所有的对象,那么此时进行MinorGC没有任何风险,然后就进行MinorGC。

若老年代无法装下新生代中所有的对象,那么此时进行MinorGC是有风险的,垃圾收集器会进行一次预测:根据以往MinorGC过后存活对象的平均数来预测这次MinorGC后存活对象的平均数。

如果以往存活对象的平均数小于当前老年代最大的连续空闲空间,那么就进行MinorGC,虽然此次MinorGC是有风险的。

如果以往存活对象的平均数大于当前老年代最大的连续空闲空间,那么就对老年代进行一次Full GC,通过清除老年代中废弃数据来扩大老年代空闲空间,以便给新生代作担保。

这个过程就是分配担保。

注意: 
1. 分配担保是老年代为新生代作担保; 
2. 新生代中使用“复制”算法实现垃圾回收,老年代中使用“标记-清除”或“标记-整理”算法实现垃圾回收,只有使用“复制”算法的区域才需要分配担保,因此新生代需要分配担保,而老年代不需要分配担保。

相关文章:

  • laravel中使一段文字,限制长度,并且超出部分使用指定内容代替
  • AWS提高声音辨识精确度为解决ML训练数据平衡性
  • iframe的高度自适应问题
  • Linux下关闭防火墙命令
  • App Store审核指南(苹果官方)(转)
  • 深入理解 async / await
  • 数组的操作
  • 前端工程师必须知道系列之:从用户输入URL到页面加载完成到底发生了什么?...
  • 软件测试上机-lab1
  • 安防摄像头有这么多种类,如何正确选择?
  • 对REST架构风格的理解
  • commander.js基本用法
  • requestAnimationFrame 使用
  • Flutter实战之自定义日志打印组件
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • angular学习第一篇-----环境搭建
  • C# 免费离线人脸识别 2.0 Demo
  • docker容器内的网络抓包
  • Linux中的硬链接与软链接
  • Mocha测试初探
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • ng6--错误信息小结(持续更新)
  • PHP 7 修改了什么呢 -- 2
  • Shell编程
  • spring security oauth2 password授权模式
  • 产品三维模型在线预览
  • 成为一名优秀的Developer的书单
  • 大数据与云计算学习:数据分析(二)
  • 双管齐下,VMware的容器新战略
  • 通过几道题目学习二叉搜索树
  • 小程序开发之路(一)
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (SpringBoot)第二章:Spring创建和使用
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (转)重识new
  • ****Linux下Mysql的安装和配置
  • .Net 8.0 新的变化
  • /var/lib/dpkg/lock 锁定问题
  • @html.ActionLink的几种参数格式
  • [04]Web前端进阶—JS伪数组
  • [2669]2-2 Time类的定义
  • [C#]扩展方法
  • [Fri 26 Jun 2015 ~ Thu 2 Jul 2015] Deep Learning in arxiv
  • [HTML]Web前端开发技术30(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页
  • [IE编程] 多页面基于IE内核浏览器的代码示例
  • [Linux]于Mac在配置Linuxserver安装Nginx+PHP
  • [noip2015 d1t2] 信息传递
  • [POJ2728] Desert King