1.总览
根据Java虚拟机规范,Java虚拟机管理的内存将分为下面五大区域
- 堆(Heap)
- 栈(Stacks)
- 方法区
- 本地方法栈
- 程序计数器
2.程序计数器
程序计数器是一块很小的内存空间,它是线程私有的,生命周期与线程相同,可以认作为当前线程的行号指示器。
假设当前线程执行到某个方法的第 n 行,此时别的线程抢占到cpu时间去执行别的任务,则当前线程挂起。如果别的线程执行完任务,当前线程恢复执行,当前线程如何确定上次执行是执行到哪里?
靠程序计数器的计数来确定。
总结:
每个线程都需有独立的一个程序计数器,不同线程之间的程序计数器互不影响,独立存储。
如果线程执行的是个java方法,那么计数器记录虚拟机字节码指令的地址。如果为native【底层方法/本地方法】,那么计数器为空。这块内存区域是虚拟机规范中唯一没有OutOfMemoryError的区域。
3. 栈(Stacks)
是线程私有的,生命周期与线程相同
每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
栈帧:
是用来存储数据和部分过程结果的数据结构,栈帧大小确定时间是在编译期确定,不受运行期数据影响。
平时说的栈一般指局部变量表部分
局部变量表:
是一片连续的内存空间,用来存放方法参数,以及方法内定义的局部变量,存放着编译期间已知的数据类型(八大基本类型和对象引用(reference类型),returnAddress类型)。
reference类型:
与基本类型不同的是它不等同本身,即使是String,内部也是char数组组成,它可能是指向一个对象起始位置指针,也可能指向一个代表对象的句柄或其他与该对象有关的位置。
returnAddress类型:
指向一条字节码指令的地址
StackOverflowError
即线程请求的栈深度大于虚拟机允许的栈深度
OutOfMemory
虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,就抛出该异常
4. 本地方法栈(Native Method Stack)
本地方法栈和虚拟机栈类似,JVM并未要求本地方法栈一定实现某种语言的方法调用,使用者可以灵活去进行调用。即使用者可以在本地方法栈调用 c / c++等别的编程语言编写的代码接口
5. 堆(Heap)
通常情况下,堆是java虚拟机管理内存最大的一块内存区域。在堆存放的对象是线程共享的,堆在物理上不要求连续,在逻辑上连续即可。
堆是垃圾收集器管理的主要区域,因此也被称为“GC堆”。
分类
- 从内存回收的角度上看,可分为新生代(Eden空间,From Survivor空间、To Survivor空间)及老年代(Tenured Gen)
- 从内存分配的角度上看,为了解决分配内存时的线程安全性问题,线程共享的JAVA堆中可能划分出多个线程私有的分配缓冲区(TLAB)
设置参数:
通过Xmx设置堆最大内存,Xms设置最小内存,Xms和Xmx一般都设置一样大小的,因为再扩容的时候会引起内存抖动,影响性能。
6. 方法区(Method Area)
是所有线程共享的内存区域,为了区分堆,又被称为非堆。
用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。
方法区是线程共享的;当有多个线程都用到一个类的时候,而这个类还未被加载,则应该只有一个线程去加载类,让其他线程等待。
方法区的大小不必是固定的,jvm可以根据应用的需要动态调整。jvm也可以允许用户和程序指定方法区的初始大小,最小和最大限制。
方法区同样存在垃圾收集,因为通过用户定义的类加载器可以动态扩展Java程序,这样可能会导致一些类,不再被使用,变为垃圾。这时候需要进行垃圾清理。
在Java8以前,方法区中定义了永久代。因为使用永久代来实现了方法区,所以被描述为堆的一个逻辑部分。在Java8的时候,永久代被替换成了元空间(Metaspace)。
运行时常量池:
是方法区的一部分,class文件除了有类的字段、接口、方法等描述信息之外,还有常量池用于存放编译期间生成的各种字面量和符号引用
7.STW(stop the world)机制
简单来说就是gc的时候,停掉除gc外的java线程。
原因:
垃圾回收是根据可达性分析算法,搜索GC Root根的引用链,将不在引用链上的对象当做垃圾回收。
设设执行某个方法的时候,此时产生了很多局部变量,刚好老年代满了需要进行Full gc,如果不停止线程,垃圾回收正在根据这些局部变量也就是GC Root根搜索引用链,此时这个方法结束了,那么这些局部变量就都会被销毁,这些引用链的GC Root根都销毁了,这些引用当然也成了垃圾对象,这样就会导致在垃圾回收的过程中还会不断的产生新的垃圾。
8. JVM内存参数设置
- -XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
- -XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发