Java基础之《JVM性能调优(10)—番外篇高并发情况下,jvm堆内存分配如何保证线程安全》

1、背景
tomcat的并发常见场景:
堆是对线程共享的,多个线程可以同时申请堆内存空间。
tomcat默认是200条线程,当200条线程同时创建对象时,必定造成争抢内存空间。
这个问题,又回到线程安全的问题了。可能马上想到加锁,如果是加锁的话,性能就大大下降了。

2、如何解决
提前为每条线程分配一小块内存,然后每条线程在创建对象的时候,直接在这块内存分配空间,就可以了,这样一来就解决了内存共享安全性的问题了。
这种方案被称之为TLAB分配,即Thread Local Allocation Buffer。这部分Buffer是从堆中划分出来的,但是是本地线程独享的。

虚拟机提前从eden区划分一块内存出来,它为每条线程分配一块TLAB空间,这样一来每条线程就有独立的内存空间。
如果线程要为对象分配内存空间,就在自己空间分配就好,避免了eden内存共享的安全性问题,提升内存分配效率。

3、每条线程分配多大的空间呢
可以通过-XX:TLABSize来自定义,默认为0
例子:
C:\Users\alex>jps
40064 Jps
29588 PreCouponPayApplication
38540 Eclipse

C:\Users\alex>jinfo -flag TLABSize 29588
-XX:TLABSize=0

默认为0,代表什么意思呢?
代表不分配?不对
默认情况下:TLAB总大小=eden区的1%,通过选项-XX:TLABWasteTargetPercent=1%
每条线程分配的空间 = eden区 * 1% / 总线程数(简单粗暴的大概公式,实际上每个垃圾回收器都会做调整而且是动态调整,比较复杂)

4、TLAB的大小
最小值:通过MinTLABSize指定,单位是字节,默认2048
最大值:不同GC中有不同的最大值
例如:G1 GC中,TLAB的最大值为大对象的大小,即是Region的一半;
ZGC中的最大值为1 / 8的Region,在大部分情况下Shenandoah GC也是每个Region大小的8分之一。
对于其他的GC,则是int数组的最大大小。

5、如何在TLAB分配对象
每条线程的TLAB空间有了,那如何在TLAB分配对象呢
虚拟机设定了一个阈值refill_waste(最大浪费空间),参数:-XX:TLABRefillWasteFraction(默认值为64,即表示使用约为1/64空间大小)
例如:TLAB=1024,那么refill_waste = 1024 / 64 = 16

情况一:当剩余空间大于16k时
当分配的对象大于16k时,直接在eden区分配。
当分配的对象小于16k时,在TLAB上分配。

情况二:当剩余空间小于16k时
虚拟机直接申请新的TLAB,在新的TLAB上分配对象,老的TLAB就废弃,等待下一次ygc回收就行。

1024已经用了1014,剩余10,原来的1024不要了,直接申请新的1024。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值