1、方法区
(1)主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码(JIT)等数据
(2)有时候也称为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载
(3)是各个线程共享的区域
(4)方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量
2、虚拟机栈
(1)虚拟机栈也就是我们平常所称的栈内存,它为 java 方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表(基本数据类型、returnAddress类型和对象引用,通过索引访问)、操作数栈(存储运算结果以及运算的操作数,通过压栈和出栈的方式访问)、动态链接和方法出口等信息
(2)线程私有的,它的生命周期与线程相同
(3)异常:
StackOverflowError:线程请求的深度大于虚拟机栈的深度
OutOfMemoryError:扩展时无法申请到足够的内存
3、本地方法栈
本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。
4、堆
JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。
堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。
(1)对于大多数应用来说,java堆是java虚拟机所管理的内存中的最大的一块
(2)java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建
(3)此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例对在这里分配内存
(4)如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,OutOfMemoryError异常
(5)是垃圾收集器管理的主要区域
(6)堆里面的分区
堆里面分为新生代(Eden+Survivor区(分为from和to区))和老生代(java8取消了永久代,采用了Metaspace),内存回收时,如果用的是复制算法,从from区复制到to区,当经过一次或者多次GC之后,存活下来的对象会被移动到老年区,当JVM内存不够用的时候,会触发Full GC,清理JVM老年区。当新生区满了之后会触发YGC(新生代内存不够用时候发生 MGC 也叫 YGC,JVM 内存不够的时候发生 FGC),先把存活的对象放到其中一个Survice区,然后进行垃圾清理。因为如果仅仅清理需要删除的对象,这样会导致内存碎片,因此一般会把Eden进行完全的清理,然后整理内存。那么下次GC的时候,就会使用下一个Survive,这样循环使用。如果有特别大的对象,新生代放不下,就会使用老年代的担保,直接放到老年代里面。因为JVM认为,一般大对象的存活时间一般比较久远(可以避免在Eden区及两个Survivor区之间发生大量的内存分配)
5、程序计数器
(1)一块较小的内存空间,它的作用是当前线程所执行的字节码行号指示器,唯一一个在jvm中没有规定任何OutOfMemoryError的区域
(2)一个处理器只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器(线程私有)
6、举例
String str = new String(“hello”);
str:栈
new出来的对象放在堆
hello存储在静态存储区(主要存放静态数据、全局数据和常量)