一、JVM内存模型
JVM内存模型按照是否为线程共享分为俩部分:线程共享区、线程独占区。
1、线程共享区
线程共享区包括俩部分:堆和方法区。线程共享区中存储的所有数据都不是线程安全的,当堆内存或者方法区内存不足时会产生OutOfMemoryError。
1.1 堆
堆是在JVM启动时进行创建的,堆中主要存储时java中的各种对象实例。
1.2 方法区
方法区中存储的是被JVM加载的一些类信息、常量信息、静态变量等信息,同时在JDK1.8之后Metaspace(元数据区)取代了方法区,同时将元数据的存储从JVM转移到了本地内存中。在JDK1.7时将常量池转移到堆中。
1.3 常量池为何转移到堆中?
JDK7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在Full GC的时候才会触发。而Full GC的触发条件比较困难只有在老年代空间不足、永久代空间不足时才会触发。这就导致StringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足,可能出现内存溢出的问题。放到堆里,能及时回收内存。
1.4 元数据区对比方法区的好处?
在JDK1.7之后方法区存储的主要的类信息以及方法信息,而类信息以及方法信息的大小难以确定,此时设置永久代过小会造成永久代内存溢出,设置过大会造成堆内存溢出。
将永久代改为元数据区时会将数据的存储区域由JVM改为本地内存中。
2、线程独占区
2.1 虚拟机栈
因为属于线程独占区,所以生命周期会随着线程的创建而创建随着线程的消亡而消亡,每一个方法的调用都会随之创建一个栈内存栈帧,方法内的所有局部变量都会存储在虚拟机栈中。
栈空间不足时会产生StackOverFlowError错误。
2.2 本地方法栈
本地方法栈和虚拟机栈非常相似,只不过虚拟机栈是在java方法调用时提供服务,而本地方法栈实在Native方法调用时提供服务。
2.3 程序计数器
对于一个方法,可能存在多个线程进行调用,此时就需要记录每个线程执行的位置,就是当前线程执行代码的字节码指示器。