java堆
基本概述
- java堆是虚拟机所管理的内存中最大的一块,在jvm启动时创建堆空间
- java堆被所有线程共享(可以划分出多个线程私有的分配缓存区TLAB),一个JVM实例只存在一个java堆
- java堆在物理上可以处以不连续的内存空间,但在逻辑上必须是连续的
核心概述
- 《Java虚拟机规范》中对Java堆的描述是:“所有的对象实例以及数组都应当在堆上分配”,但是由于即时编译技术的进步、逃逸分析技术的日渐强大,栈上分配、标量替换优化手段使得java对象实例都在堆上分配也变得不绝对了
- 数组和对象可能永远不会存储在栈上,因为栈针局部变量表中保存的是引用类型对象的引用指针,这个指针指向对象或数组在堆中的位置
- 方法运行结束后,不会回收堆中的对象,只有等GC执行后,才会根据条件进行回收
- 堆是GC的重点区域
内存结构
堆空间大小
- 堆的初始空间大小在JVM启动时就已经设定好了
“-Xms”用来表示堆区的起始内存,等价于-XX:InitialHeapSize
“-Xmx”用来表示堆区的最大内存,等价于-XX:MaxHeapSize
- 一旦堆区内对象的存储空间所需要的大小超过了-Xmx指定的大小,就会报OOM异常
- 一般情况下,将-Xms和-Xmx配成一样的大小,也就是说堆区大小是固定的;这样可以在GC清理完堆区后不需要重新分配计算堆的大小,从而提高性能
- 默认情况下,初始堆内存大小 = 电脑初始内存 / 64;堆内存最大 = 电脑初始内存 / 4
TLAB(Thread Local Allocation Buffer)
为什么要有TLAB?
堆区是共享区域,任何线程都可以访问堆的共享区域,而由于对象创建十分频繁,所以并发环境下创建对象是线程不安全的,可能存在多个线程共同操作同一地址,为了解决这个问题,需要使用加锁等机制,而加锁是比较耗时的操作
经过上述问题,jvm在堆区的新生代的Eden空间中提供了线程私有缓冲区域TLAB,它不仅可以避免一系列的线程安全问题,还能够提升内存分配的吞吐量,因此我们将这种策略称之为快速分配策略
基本概述
- 尽管不是所有对象实例都能在TLAB中成功分配内存,但JVM仍然将其作为内存分配的首选
- 可以通过“-XX:UseTLAB”设置是否开启TLAB,可以通过“-XX:TLABWasteTargetPercent”设置TLAB空间所占Eden空间的百分比
- 默认情况下,TLAB空间的内存非常小,仅占有整个Eden空间的1%,
- 一旦对象在TLAB空间分配内存失败,JVM就会尝试通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存