JDK1.8 之前
程序计数器, Java 虚拟机栈, 本地方法栈, 堆, 方法区, 运行时常量池, 直接内存
JDK1.8 之后
程序计数器, Java 虚拟机栈, 本地方法栈, 堆, 元空间, 直接内存
线程私有的:
线程共享的:
在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常被分为下面三部分:
老年代与新生代比例为 2 : 1。新生代分为一个Eden和两个 Survivor 区域,比例为8:1:1。
在 JDK1.8 之后,永生代(Permanent Generation)被移除,取而代之的是元空间
默认 15 次.
对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold
来设置。
1.引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。
这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。
2.可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
Serial 收集器
ParNew 收集器
Parallel Scavenge 收集器
Serial Old 收集器
Parallel Old 收集器
CMS 收集器
G1 收集器
加载
验证
1. 文件格式验证 2. 元数据验证 3. 字节码验证 4. 符号引用认证
准备
为类变量分配内存并设置类变量初始值
解析
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符7类符号引用进行。
初始化
初始化阶段是执行类构造器 <clinit> ()
方法的过程
JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader
:
%JAVA_HOME%/lib
目录下的 jar 包和类或者或被 -Xbootclasspath
参数指定的路径中的所有类。%JRE_HOME%/lib/ext
目录下的 jar 包和类,或被 java.ext.dirs
系统变量所指定的路径下的 jar 包。每一个类都有一个对应它的类加载器。系统中的 ClassLoder 在协同工作的时候会默认使用 双亲委派模型 。即在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 loadClass()
处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader
中。当父类加载器无法处理时,才由自己来处理。当父类加载器为 null 时,会使用启动类加载器 BootstrapClassLoader
作为父类加载器。
双亲委派模型保证了Java程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API 不被篡改。如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object
类的话,那么程序运行的时候,系统就会出现多个不同的 Object
类。
为了避免双亲委托机制,我们可以自己定义一个类加载器,然后重写 loadClass()
即可。
除了 BootstrapClassLoader
其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader
。如果我们要自定义自己的类加载器,很明显需要继承 ClassLoader
。
-Xms2G -Xmx5G
jps
jstat
jinfo
jconsole