1,程序计数器
1.1 用于存储每个线程下一步将执行的jvm指令
1.2 如该方法是Native的,则不存储任何信息
1.3 Java是多线程机制离不开寄存器
1.4 不会OutOfMemoryError
2.stack (虚拟机栈):
2.1 栈是线程私有的,每个线程在创建时都会创建栈(同时创建,生命周期与线程相同)
2.2 栈中存放当前线程中基本类型变量、操作数栈、动态链接、方法出口等信息;
2.3 每个方法被调用到执行完成的过程就对应着一个栈桢在jvm中入栈到出栈的过程;
2.4 非基本类型对象在栈上仅存一个指向堆的地址
2.5 Java虚拟机栈可能出现两种类型的异常(-Xss 分配每个线程内存大小):
线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。
虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。
3.heap(堆)
3.1 所有线程共享的区域,在jvm启动时创建,是存储对象实例以及数组的区域,
3.2 堆内存需要GC进行回收,目前收集器多使用分代算法,所以可细分为新生代和老年代
3.3 新生代:
Eden(伊甸园)区:新对象分配内存的地方 (先使用每个线程独享的TLAB(Thread Local Allocation Buffer)分配,不需要加锁)
Survival(幸存者) from & to:eden区内存用完时会发生一次Minor GC(young gc);
第一步:Eden区和Survivalfrom区会把一些仍然存活的对象复制进Survival to区,并清除内存
第二步:Survival to区会把一些存活得足够旧的对象移至年老代
第三步:GC完成后,from区就会和to区发生互换
注意:那么from区 在jvm发生第一次young gc之前应该是空的吧?
3.4 老年代:经过多次垃圾回收依然存活的对象会被转移到老年代 (老年代会发生full gc)
3.5 堆内存不足会发生 OutOfMemoryError
4,方法区
4.1 方法区同堆一样,是所有线程共享的内存区域
4.2 存储类的结构信息。例如运行时常量池,成员变量和方法等。
其中类的元信息存储在元空间中(本地内存),静态常量和常量池并入堆中。
4.3 运行时常量池:方法区的一部分,用于存放编译期间生成的各种字面量和符号引用
4.3 永久代:jdk8真正开始废弃永久代,而使用元空间(Metaspace)
5,本地方法栈
与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,可能底层调用的c或者c++
附:JVM 三大性能调优参数
-Xss规定了每个线程堆栈的大小。一般情况下256K是足够了。影响了此进程中并发线程数大小。
-Xms初始的Heap的大小。
-Xmx最大Heap的大小。
在很多情况下,-Xms和-Xmx设置成一样的。这么设置,是因为当Heap不够用时,会发生内存抖动,影响程序运行稳定性。
三大以外:
-XX:MaxTenuringThreshold :来设置晋升的年龄(去老年代的年龄)
-XX PertenureSizeThreshold :大于这个参数的对象直接在老年代中分配内存,避免大量复制算法
-XX:PermSize和-XX:MaxPermSize:在jdk1.6及以前常量池分配在永久代中。可限制方法区大小