程序计数器是线程私有的,为了记住下一条JVM指令的地址,并且是java虚拟机规范中唯一没有规定任何OOM(Out Of Memery Error)的内存区域
程序计数器指定了下一条需要指定的指令,每个线程都有一个程序计数器,程序计数器保证了程序的有序执行.
与程序计数器一样,栈也是线程私有的,栈的生命周期和线程是绑定的,并不需要垃圾回收器的介入,线程结束,相应的栈空间也跟着回收
线程内的每个方法执行时都会创建一个栈帧,存入线程对应的栈中,并且每个线程只有一个活动的栈帧.对应着正在执行的方法.方法执行完毕,相应的栈帧出栈.简而言之,方法的执行与退出对应着栈帧的入栈与出栈.
栈帧包括:局部变量表,操作数栈,动态链接,方法出口等
局部变量表顾名思义,存放局部变量的,以4字节32位为一个局部变量空间,除了long double类型,其他类型都占一个局部变量空间.
StackOverflowError
根据<深入了解JAVA虚拟机第二版>描述,如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError.栈的深度受栈大小和栈帧.通过设置虚拟机参数-Xss228k 并使用递归来模拟StackOverflow
public class Hello {
public static void main(String[] args) {
System.out.println("hello world");
sof(1);
}
public static void sof(int i) {
System.out.println(i);
sof(++i);
}
}
public class Hello {
public static void main(String[] args) {
System.out.println("hello world");
sof(1);
}
public static void sof(int i) {
Object o = new Object();
System.out.println(i);
sof(++i);
}
}
OutOfMemeryError
如果虚拟机栈可以动态扩展,当申请不到需要的内存时会跑出OutOfMemeryError
栈是是线程私有的,正常情况下栈中的数据通常也就是方法中的局部变量是线程安全的,但是,如果方法中的局部变量同时可以被方法外访问到,则需要考虑线程安全问题,通常情况是一个对象作为参数传入方法或者作为返回值离开方法.
堆是线程共享的,主要用于存放对象实例及数组,如果说栈中存的只对象的门牌号(reference),那么堆中存的便是门牌号(reference)所指向的具体的房子实体.垃圾回收器(GC)主要便工作于此.
配合垃圾回收器工作,堆分为了:
Eden区: 新分配的对象在此
To Survivor 区 :Eden区以及From Survivor躲过垃圾回收后的对象移动到此.此为暂存区,储存逃过一劫对象
From Survivor 区: To Survivor 区中对象移动到此目的为腾出To区空间,为下次周转做准备.,From区中所有对象年龄加一.年龄到一定程度(最大15)进入老年代(Old ),注:年龄并未唯一进入老年代条件
以上三个统称新生代,minor GC就发生在这些内存区域
老年代:常用对象根据条件进入老年代,减少垃圾回收次数提高效率. 老年代发生GC 为full GC,在发送Full GC之前会调用minor GC
没有进行规范,不同虚拟机各自实现
JDK1.6叫方法去,1.8开始叫元空间
JDK 1.6:
JDK 1.8
方法区是所有jvm线程共享的,储存了类的相关信息,包括运行时常量池,类加载器,类成员变量,方法数据,方法(包括构造器)代码.
方法区在启动时被创建,java虚拟机规范没有强制规定方法区的位置,就如,HotSpot 虚拟机JDK1.6和1.8的方法区/元空间位置不一样,JDK1.6方法区在堆中划分一块内存实现,也叫作永久代(Permanent Generation) .JDK1.8中将方法区改名为元空间,并在在操作系统内存中实现.
总结:方法区各个虚拟机各自实现,永久代指HotSpot JDK1.6及以前的方法区实现,之后改名元空间,并在操作系统内存中实现.
针对HotSpot虚拟机
JDK1.6 和 JDK1.8 方法区的改变:
1.6 | 1.8 | |
---|---|---|
名称 | 方法区(Method Area)/永久代(Permanent) | 元空间(MateSpace) |
内容 | 运行时常量池(包含StringTable),类属性,类加载器 | 运行时常量池(不包含StringTable),类属性,类加载器 |
位置 | java堆中 | 操作系统内存中 |
stringtab位置 | 方法区内常量池中 | Java堆中 |
方法区/元空间主要储存类信息,所以当类加载过多就会导致OOM
JVM设置元空间大小参数(1.8): -XX:MaxMatesapceSize=8m
JVM设置方法区大小参数(1.86): -XX:MaxPermeSize=8m
通过javap -v xxx.class 命令可以查看字节码文件的常量池
常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数等信息,
运行时常量池,常量池是class文件中的一张表,当该类信息被加载就会将类中常量池信息放入运行时常量池,并把符号地址变为真实地址.