一、JVM内存结构图
JDK1.7以前
JVM内存结构主要有三大块:堆内存、方法区和栈。
- 堆内存是JVM中最大的一块,是线程共享的区域,由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代的这3种空间年轻代按照8:1:1的比例来分配
- 方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆)
- 栈又分为java虚拟机栈和本地方法栈主要用于方法的执行
JDK1.8以后
以前的方法区(或永久代),用来存放class,Method等元数据信息,但在JDK1.8已经没有了,取而代之的是MetaSpace(元空间),元空间不在虚拟机里面,而是直接使用本地内存。
为什么要用元空间代替永久代?
- 类以及方法的信息比较难确定其大小,因此对于永久代的指定比较困难,太小容易导致永久代溢出,太大容易导致老年代溢出。
- 永久代会给GC带来不需要的复杂度,并且回收效率偏低。
二、JVM架构
1.Class Loader类加载器
负责加载.class文件,至于class文件是否可以运行,则由Execution Engine决定。JVM01-类加载机制
2.Execution Engine 执行引擎
执行包在装载类的方法中的指令,也就是方法。
3.Runtime data area 运行数据区
从整个计算机内存中开辟一块内存存储Jvm需要用到的对象,变量等,分为:方法区,堆,虚拟机栈,程序计数器,本地方法栈。
3.1 Java堆(Heap)
- Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
- 此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
- Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。
- Java堆中还可以细分为:新生代和老年代;新生代又可以分为Eden空间、From Survivor空间、To Survivor空间。
堆是理解Java GC机制最重要的区域,没有之一
结构:新生代(Eden区+2个Survivor区) 老年代
新生代:新创建的对象——>Eden区
GC之后,存活的对象由Eden区 Survivor区0进入Survivor区1
再次GC,存活的对象由Eden区 Survivor区1进入Survivor区0
老年代:对象如果在新生代存活了足够长的时间而没有被清理掉(即在几次Young GC后存活了下来,默认是15次),则会被复制到老年代
如果新创建对象比较大(比如长字符串或大数组),新生代空间不足,则大对象会直接分配到老年代上(大对象可能触发提前GC,应少用,更应避免使用短命的大对象)
老年代的空间一般比新生代大,能存放更多的对象,在老年代上发生的GC次数也比年轻代少
- 通过-Xmx和-Xms可分配堆内存大小。
- 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
3.2 方法区(Method Area)
- 与Java堆一样,是各个线程共享的内存区域。用于存储虚拟机加载的:静态变量+常量+类信息+运行时常量池
- 默认最小值为16MB,最大值为64MB,可以通过
-XX:PermSize
和-XX:MaxPermSize
参数限制方法区的大小 - JDK1.6使用永久代实现方法区;JDK1.8使用元空间实现方法区
JDK1.8不再使用 JVM内存结构管理方法区的内存,而使用操作系统的本地内存(物理内存)
3.3 程序计数器(Program Counter Register)
每个线程都有一个程序计算器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。
3.4 JVM栈(JVM Stacks)
- Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。
- 每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
- 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。
3.5 本地方法栈(Native Method Stacks)
- Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies
- 本地方法栈与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务
三、JVM 调参
jvm常用参数 | 含义 |
---|---|
-XX:+PrintGC/-verbose:gc | 打印GC的简要信息 |
-XX:+PrintGCDetails | 打印GC的详细信息 |
-XX:+PrintGCDateStamps | 打印GC发生的时间 |
-Xloggc:log/gc.log | 指定GC的log位置,以文件输出 |
-XX:+PrintHeapAtGC | 每一次GC后都打印堆信息 |
-XX:+HeapDumpOnOutOfMemoryError | 当JVM发生OOM时,自动生成DUMP文件 |
-XX:HeapDumpPath=${目录} | 生成的DUMP文件的存放位置 |
-Xms | 初始堆大小,默认是物理内存的1/64 |
-Xmx | 最大堆大小 默认是物理内存的1/4 |
-Xmn | 年轻代的大小,默认整个堆的3/8 |
-Xss | 设置每个线程的堆栈大小 |
-XX:MetaspaceSize | 初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值 |
-XX:MaxMetaspaceSize | 空间最大内存,默认是没有限制的 |
-XX:MaxDirectMemorySize | 限制堆外内存的使用 |
-XX:+DisableExplicitGC | 禁用 System.gc 显式FullGC |
-XX:ReservedCodeCacheSize | 调整JIT编译代码缓存 |
-XX:PretenureSizeThreshold | 大于这个值的对象直接在老年代分配 |
-XX:+PrintGCApplicationConcurrentTime | 打印每次垃圾回收前,程序未中断的执行时间 |
-XX:+PrintGCApplicationStoppedTime | 打印垃圾回收期间程序暂停的时间 |