4、jvm内存分配与回收-堆

内存分配

过程

1、当执行new指令时,首先会检查new指令后的类是否已被加载,否则会触发加载动作。

2、类检测没问题后,开始分配内存,由于堆内有不同的区域,所以分配方式也会不同。

        a.指针碰撞,用于内存区域是规整的,分界点一侧是已分配好的内存区域,另一侧是未分配的,只需要将分界点向未分配区域移动待分配长度即可。

        b.空闲列表,若内存区域是散乱的,已经无法使用指针碰撞,就需要记录每一个空间区域,匹配一个足够大的区域进行分配,然后更新列表。

堆内区域,是所有线程共享的,每个线程都在分配内存,就会产生并发问题。jvm解决并发的方法:

        a.CAS,通过CAS不停的尝试去获取内存区域

        b.TLAB(thread local allocation buffer)本地线程分配缓存,在eden区为线程预先划分一小块缓存,避免线程竞争,当超出缓存时,就按之前的策略分配。该策略是默认开启的,也可关闭-XX:+/-UseTLAB。 -XX:TLABSize可指定缓存大小

3、初始化0值, 提前为分配到的空间赋0值,不包括对象头,即使直接使用对象,也不会报错。

4、设置对象头,对象头主要包含2大部分,若是数组对象,还会附带一个长度

com.maomaotou.test.Cat object internals:
 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
     //以下为头部区域==========================
     //0-8 32位,MarkWord
      0     4                     (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      //该4位用于标记对象的类指针
      8     4                     (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     //以下为对象主体区域==========================
     //分别为name属性和age属性
     12     4    java.lang.String Cat.name                                  (object)
     16     4   java.lang.Integer Cat.age                                   null
     //头+身,总计20字节,不满足8的倍数,需要对齐4字节
     20     4                     (loss due to the next object alignment)
Instance size: 24 bytes

以上是32位操作系统的内存分配。

在64位操作系统中,除了MarkWord会加长很多外,其余差别不大, 主要是因为jvm使用了指针压缩技术。使指针继续保持32位的长度。

指针压缩

对应设置-XX:+/-UseCompressedOops 默认开启

如同操作系统,当堆内存小于4G时,使用32位指针就足够了,多余的高位指针抛弃即可。

当内存大于4G时,jvm通过压缩指针,继续保持32位,在到寄存器的时候进行解压还原指针。

压缩指针最大也只能压缩35位,也就是2^35 = 32G,所以当堆内存大于32G时,压缩指针将失效,jvm会使用64位指针,会造成内存膨胀,移动缓慢。所以尽量让堆内存不要超过32G。

JVM在内存分配时的优化

1、Eden区分配内存

新生对象会优先分配eden区,eden区内存规整,可使用指针碰撞,当eden区已满时会触发minor gc,minor gc效率较高,回收快。

eden承担主要分配工作,需要设置的较大,survive区用于存放未被回收的,够用即可。

eden区和survive区的比例默认是8:1:1,并且是动态的,可通过配置禁止动态

-XX:-UseAdaptiveSizePolicy

2、大对象直接分配到老年代

当对象较大时,为了防止大对象来回在eden区和survive区之间移动,可直接分配到老年代,大对象的阈值可通过参数设定

-XX:PretenureSizeThreshold=1000000 -XX:+UseSerialGC

仅在Serial 和ParNew回收器有效。

3、对象年龄

对象每经过一次minor gc会在头部年龄+1,默认达到15时,会转移动老年代,可通过设置修改默认值

-XX:MaxTenuringThreshold

age在头部只有2位,最大也只能到15

4、对象年龄动态判断

当移入到survive区的总大小超过survive的50%时,部分对象会被直接移到老年代。

可通过设置设置比例-XX:TargetSurvivorRatio

5、老年代空间分配担保

每次minor gc时,都会计算老年代剩余可用空间

如果该空间小于现有年轻代对象只和。会根据参数执行不同策略-XX:-HandlePromotionFailure

有设置(默认有)

会用老年代剩余空间,和每次minor gc 平均进入老年代的大小做比较,如果剩余空间小,则会直接执行full gc,节约一次minor gc

未设置

直接和年轻代对象总和比。

内存回收

内存标记

引用计数法

可达性算法

四种引用

强,软,弱,虚

软,缓存常用,在GC无可用内存时会被回收。

finalize() 尽量不用

类的回收

需要满足条件

1、所有实例已被回收

2、类的classLoader已被回收

3、class没有被引用

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值