JAVA内存

Java内存区域

1.Java内存结构

在这里插入图片描述

  • 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。方法区里面由运行时常量(static final等),会发生out of memory
  • 虚拟机栈:Java栈,存放栈帧(栈帧中存放的是局部变量、操作数栈、动态链接、方法出口等信息,栈帧中的局部变量表存放基本类型+对象引用+returnAddress,局部变量所需的内存空间在编译期间就完成分配了,因为基本类型和对象引用等都能确定占用多少slot,在运行期间也是无法改变这个大小的。),栈帧里存放局部变量表等信息,方法执行到结束对应着一个栈帧的入栈到出栈,会发生StackOverflow。
  • 本地方法栈:为虚拟机调用native方法服务的,会发生StackOverflow。
  • 堆:jvm中内存最大的部分,几乎所有对象实例的分配都在此处,会发生out of memory
  • 程序计数器:当前线程所执行字节码的行号指示器,如果改方法是native的,值为undefined,无oom或sof。

2.对象创建时堆内存分配算法

  • 指针碰撞:堆内存的绝对工整
  • 空闲列表:JVM维护一个列表 ,记录哪些内存块是可用的,当对象创建时从列表中找到一块足够大的空间划分给新生对象,并将这块内存标记为已用内存

3.对象定位

  • 直接指针:栈上的引用直接指向堆中的对象
  • 句柄:Java堆中单独划分一块内存空间作为句柄池,栈上的引用存储的时句柄地址,而不是真实对象的地址。对象在堆中的位置发生移动,栈上的引用也无需发生变化。

4.判断对象是否回收的算法

  • 引用计数法:给对象添加一个引用计数器,引用它时计数器+1,引用失效时计数器-1,计数器为0时,此对象为垃圾对象。有内存泄漏的bug,如循环引用
  • 可达性分析法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的垃圾对象。

5.Java堆内存组成部分

  • 1.7及以前,堆大小 = 新生代 + 老年代 + Permanent Generation
  • 1.8及以后,堆大小 = 新生代 + 老年代 + Metaspace

6.堆栈分开的原因

  • 栈代表了处理逻辑,堆代表了存储数据,分开后逻辑更清晰,面向对象模块化思想。栈是线程私有,堆是线程共享区,这样分开也节省了空间,比如多个栈中的地址指向同一块堆内存中的对象。
  • 栈是运行时的需要,比如方法执行到结束,栈只能向上增长,因此会限制住栈存储内容的能力,而堆中的对象是可以根据需要动态增长的。

7.堆栈的区别

物理地址

  • 堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩)
  • 栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。

内存

堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。

存放内容

  • 堆存放的是对象的实例和数组。因此该区更关注的是数据的存储
  • 栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。
    静态变量放在方法区,静态的对象还是放在堆。

程序可见度

  • 堆对于整个应用程序都是共享、可见的。
  • 栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。

8.内存分配策略

对象的内存分配通常是在 Java 堆上分配,对象主要分配在新生代的 Eden 区,如果启动了本地线程缓冲,将按照线程优先在 TLAB 上分配。少数情况下也会直接在老年代上分配。遵循以下规则:

对象优先在 Eden 区分配

对象都在新生代 Eden 区分配。当 Eden 区分配没有足够的空间进行分配时,虚拟机将会发起一次 Minor GC。如果本次 GC 后还是没有足够的空间,则将启用分配担保机制在老年代中分配内存。

  • Minor GC 是指发生在新生代的 GC,因为 Java 对象大多都是朝生夕死,所有 Minor GC 非常频繁,一般回收速度也非常快;
  • Major GC/Full GC 是指发生在老年代的 GC,出现了 Major GC 通常会伴随至少一次 Minor GC。Major GC 的速度通常会比 Minor GC 慢 10 倍以上。

大对象直接进入老年代

所谓大对象是指需要大量连续内存空间的对象,频繁出现大对象是致命的,会导致在内存还有不少空间的情况下提前触发 GC 以获取足够的连续空间来安置新对象。前面我们介绍过新生代使用的是标记-清除算法来处理垃圾回收的,如果大对象直接在新生代分配就会导致 Eden 区和两个 Survivor 区之间发生大量的内存复制。因此对于大对象都会直接在老年代进行分配。

长期存活对象将进入老年代

虚拟机采用分代收集的思想来管理内存,那么内存回收时就必须判断哪些对象应该放在新生代,哪些对象应该放在老年代。因此虚拟机给每个对象定义了一个对象年龄的计数器,如果对象在 Eden 区出生,并且能够被 Survivor 容纳,将被移动到 Survivor 空间中,这时设置对象年龄为 1。对象在 Survivor 区中每「熬过」一次 Minor GC 年龄就加 1,当年龄达到一定程度(默认 15) 就会被晋升到老年代。

动态判断对象的年龄

动态判断对象的年龄,如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。无需等到-XX:MaxTenuringThreshold参数要求的年龄。

空间分配担保

每次进行YGC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行YGC,如果false则进行Full GC。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值