1.本地方法栈(Native) 调用c部分
2.程序计数器 (程序代码行数) JVM支持多个线程同时运行,每个线程都有自己的程序计数器。倘若当前执行的是 JVM 的方法,则该寄存器中保存当前执行指令的地址;倘若执行的是native 方法,则PC寄存器中为空。
3.栈 stack :每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。栈的大小可以固定也可以动态扩展。当栈调用深度大于JVM所允许的范围,会抛出StackOverflowError的错误
4.堆 heap(存实际对象) 堆内存是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过 GC 进行回收。当申请不到空间时会抛出 OutOfMemoryError
5方法区 :主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。 关于方法区内存溢出的问题会在下文中详细探讨
双亲委派机制 : 一层一层的让父类去加载,如果顶层的加载器不能加载,然后再向下类推
// BootStrap (最顶层) 01 java.lang.String rt.jar /jdk/jre/lib/ 下面的jar
// ExtClassLoader 02 /jdk/jre/lib/ext/下面的jar
// AppClassLoader 03 //AppClassLoader
// 双亲委派机制 可以保护java的核心类不会被自己定义的类所替代
// -XX:+TraceClassLoading
public static void main(String[] args) {
Object o = new Object(); // jdk 自带的
Demo01 demo01 = new Demo01(); // 实例化一个自己定义的对象
// null 在这里并不代表没有,只是Java触及不到!Bootstrap
System.out.println(o.getClass().getClassLoader()); // null
System.out.println(demo01.getClass().getClassLoader()); // AppClassLoader
System.out.println(demo01.getClass().getClassLoader().getParent()); // ExtClassLoader
System.out.println(demo01.getClass().getClassLoader().getParent().getParent()); // null
}
3.JDK1.8之后和1.7区别
JDK1.7之前
永久代:用于存储一些虚拟机加载类信息,常量,字符串、静态变量等等。。。。这些东西都会放到永久代中;
永久代大小空间是有限的:如果满了 OutOfMemoryError:PermGen
JDK1.8之后
彻底将永久代移除 HotSpot jvm ,Java Heap 中或者 Metaspcace(Native Heap)元空间;
元空间就是方法区在 HotSpot jvm 的实现;
方法区重要就是来存:类信息,常量,字符串、静态变量、符号引用、方法代码。。。。。。
元空间和永久代,都是对JVM规范中方法区的实现。
元空间和永久代最大的区别:==元空间并不在Java虚拟机中,使用的是本地内存!==
-XX:MetasapceSize10m
Method Area 方法区 是 Java虚拟机规范中定义的运行是数据区域之一,和堆(heap)一样可以在线程之间共享!栈不能共享
天上飞的理念都会有落地的实现!
JDK1.7之前
永久代:用于存储一些虚拟机加载类信息,常量,字符串、静态变量等等。。。。这些东西都会放到永久代中;
永久代大小空间是有限的:如果满了 OutOfMemoryError:PermGen
JDK1.8之后
彻底将永久代移除 HotSpot jvm ,Java Heap 中或者 Metaspcace(Native Heap)元空间;
元空间就是方法区在 HotSpot jvm 的实现;
方法区重要就是来存:类信息,常量,字符串、静态变量、符号引用、方法代码。。。。。。
元空间和永久代,都是对JVM规范中方法区的实现。
元空间和永久代最大的区别:==元空间并不在Java虚拟机中,使用的是本地内存!==
-XX:MetasapceSize10m
Java7之前:
Heap 堆,一个JVM实例中只存在一个堆,堆的内存大小是可以调节的。
可以存的内容:类、方法、常量、保存了类型引用的真实信息;
分为三个部分:
新生区:Young (Eden-s0-s1) Eden-s0-s1默认8:1:1
养老区:Old Tenure 大对象(-XX:PretenureSizeThreadshold=1024)直接进入老年代,在新生代里面长期存活的对象进入老年代。老年代对应的是major gc。
新生区和养老区默认1:2
永久区:Perm
堆内存在==逻辑上==分为三个部分:新生、养老、永久(JDK1.8以后,叫元空间)
物理上只有 新生、养老;元空间在本地内存中,不在JVM中!
GC 垃圾回收主要是在 新生区和养老区,又分为 普通的GC 和 Full GC,如果堆满了,就会爆出 OutOfMemory;
新生区
新生区 就是一个类诞生、成长、消亡的地方!
新生区细分: Eden、s(from to),所有的类Eden被 new 出来的,慢慢的当 Eden 满了,程序还需要创建对象的时候,就会触发一次轻量级GC;清理完一次垃圾之后,会将活下来的对象,会放入幸存者区(),....... 清理了 20次之后,出现了一些极其顽强的对象,有些对象突破了15次的垃圾回收!这时候就会将这个对象送入养老区!运行了几个月之后,养老区满了,就会触发一次 Full GC;假设项目1年后,整个空间彻彻底底的满了,突然有一天系统 OOM,排除OOM问题,或者重启;
Sun HotSpot 虚拟机中,内存管理(分代管理机制:不同的区域使用不同的算法!)
Eden from to
99% 的对象在 Eden 都是临时对象;
养老区
15次都幸存下来的对象进入养老区,养老区满了之后,触发 Full GC
默认是15次,可以修改!
永久区(Perm)
放一些 JDK 自身携带的 Class、Interface的元数据;
几乎不会被垃圾回收的;
OutOfMemoryError:PermGen 在项目启动的时候永久代不够用了?加载大量的第三方包!
JDK1.6之前: 有永久代、常量池在方法区;
JDK1.7:有永久代、但是开始尝试去永久代,常量池在堆中;
JDK1.8 之后:永久代没有了,取而代之的是元空间;常量池在元空间中;
闲聊:方法区和堆一样,是共享的区域,是JVM 规范中的一个逻辑的部分,但是记住它的别名 非堆
元空间:它是本地内存!
1.8之后permSize和MaxPermSize被-XX:MetaspaceSize和-XX:MaxMetaspaceSize取代
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集
-XX:+PrintGCDetails:输出详细的GC处理日志
-XX:+PrintGCTimeStamps:输出GC的时间戳信息
-XX:+PrintGCDateStamps:输出GC的时间戳信息(以日期的形式)
-XX:+PrintHeapAtGC:在GC进行处理的前后打印堆内存信息
-Xloggc:(SavePath):设置日志信息保存文件
Minor GC ,Full GC 触发条件
Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法去空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
Java堆:所有线程共享。虚拟机启动时创建。存放对象实力和数组。所占内存最大。分为新生代(Young区),老年代(Old区)。新生代分Eden区,Servior区。Servior区又分为From space区和To Space区。Eden区和Servior区的内存比为8:1。 当扩展内存大于可用内存,抛OOM。
GC流程 ,对象如何晋升到老年代
对象优先在新生代区中分配,若没有足够空间,Minor GC; 大对象(需要大量连续内存空间)直接进入老年态;长期存活的对象进入老年态。如果对象在新生代出生并经过第一次MGC后仍然存活,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。
老年代的垃圾回收称作Full GC。老年代所占用的内存大小为-Xmx对应的值减去-Xmn对应的值。
/**
* 默认情况:
* maxMemory : 1808.0MB (虚拟机试图使用的最大的内存量 一般是物理内存的 1/4)
* totalMemory : 123.0MB (虚拟机试图默认的内存总量 一般是物理内存的 1/64)
*/
// 我们可以自定堆内存的总量
// -XX:+PrintGCDetails; // 输出详细的垃圾回收信息
// -Xmx: 最大分配内存; 1/4
// -Xms: 初始分配的内存大小; 1/64
// -Xmx1024m -Xms1024m -XX:+PrintGCDetails
public class Demo01 {
public static void main(String[] args) {
// 获取堆内存的初始大小和最大大小
long maxMemory = Runtime.getRuntime().maxMemory();
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("maxMemory="+maxMemory+"(字节)、"+(maxMemory/1024/(double)1024)+"MB");
System.out.println("totalMemory="+totalMemory+"(字节)、"+(totalMemory/1024/(double)1024)+"MB");
}
}
GCDump分析内存泄露分析
安装JProfile
运行时加上参数:-XX:+HeapDumpOnOutOfMemoryError 生成的文件在项目根目录下/同级