03、JVM实战:内存区域:堆

03–内存区域–堆


1、结构图

*

2、堆

2.1、Java堆的特征

1、 几乎所有的对象都存放在堆上并且java堆是完全自动化管理的,通过垃圾回收机制,垃圾对象会被自动清理;
2、 可能出现OutOfMemoryError异常;
3、 java堆最为常见结构是将整个java堆分为新生代和老年代;

1、 新生代存放新生对象或者年龄不大的对象;
2、 老年代则存放老年对象;
3、 新生代有可能分为eden区(8)、from区(1)、to区(1),from和to区是大小相同、可以互换角色的内存空间;
4、 所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的;
5、 SunHotspotJVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(ThreadLocalAllocationBuffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配;

1、 TLAB仅作用于新生代的EdenSpace,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效;
6、 所有新创建的对象都将会存储在新生代中如果新生代的数据在一次或多次GC后存活下来,那么将被转移到老年代;
7、 新创建的对象总在EdenSpace;

2.2、堆空间的一般结构

*

1、 在绝大多数情况下,对象首先分配在eden区,在一次新生代回收之后,如果对象还存活,则进入from或者to,每经过一次新生代回收,对象如果存活,它的年龄就会加1当对象的年龄达到一定条件(默认年龄15)后,就会被认为是老年对象,从而进入老年代;
2、 默认比例:年轻代:老年代=1:2;

2.3、栈到堆的关联过程

*

2.4、案例

*

SimpleHeap实例本身分配在堆中,描述SimpleHeap类的信息存放在方法区,main函数中的s1 s2局部变量存放在java栈上,并指向堆中两个实例。

2.5、堆溢出代码案例

public class TestHeap {
	// 分配128Kb的内存给数组,方便堆内存溢出
	byte[] bis = new byte[128 * 1024];
 
	public static void main(String[] args) {
		List<TestHeap> list = new ArrayList<>();
		//增加堆内存
		while (true) {
			list.add(new TestHeap());
		}
	}
}
结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.fei.zhou.day1.TestHeap.<init>(TestHeap.java:8)
at com.fei.zhou.day1.TestHeap.main(TestHeap.java:14)

2.6、总结:

1、 堆中存的是对象;

1、 对象的大小是不可估计的,或者说是可以动态变化的;
2、 栈中存的是基本数据类型和堆中对象的引用;

1、 一个对象引用大小是4btye;