From: JVM 系列阅读笔记:https://liudongdong1.github.io/tags/jvm/
.1. 简述垃圾回收机制
.2. 内存模型
Java 堆
是被所有线程共享的一块内存区域
,在虚拟机启动时创建
。此内存区域的唯一目的就是存放对象实例
,几乎所有的对象实例都在这里分配内存。- 方法区(Method Area)是
各个线程共享的内存区域
,它用于存储已被虚拟机加载
的类信息、常量、静态变量、即时编译器编译后的代码
等数据。 - 程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是
当前线程所执行的字节码的行号指示器
。 - Java 虚拟机栈(Java Virtual Machine Stacks)也是
线程私有的
,它的生命周期与线程相同
。虚拟机栈描述的是 Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等
信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 - 本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而
本地方法栈则是为虚拟机使用到的 Native 方法
服务。
.3. 如何判断对象死完:
.4. GC 回收机制
1. 标记清除法:
2. 复制算法:
3. 标记整理算法
4. 分代收集算法
当前商业虚拟机都采用分代收集的垃圾收集算法。分代收集算法,顾名思义是根据对象的存活周期将内存划分为几块。一般包括年轻代、老年代 和 永久代,如图所示:
.5.垃圾回收三色标记法:
.6. CMS垃圾回收器介绍:
.7. JVM 俩个内存
1. 栈内存
2. 堆内存
.8. 类加载器
启动类加载器
:Bootstrap ClassLoader,负责加载存放在 JDK\jre\lib (JDK 代表 JDK 的安装目录,下同) 下,或被 - Xbootclasspath 参数指定的路径中的,并且能被虚拟机识别的类库
扩展类加载器
:Extension ClassLoader,该加载器由 sun.misc.Launcher$ExtClassLoader 实现,它负责加载 DK\jre\lib`ext 目录` 中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如 javax.* 开头的类),开发者可以直接使用扩展类加载器。应用程序类加载器
:Application ClassLoader,该类加载器由 sun.misc.Launcher$AppClassLoader 来实现,它负责加载用户类路径
(ClassPath)所指定的类,开发者可以直接使用该类加载器避免重复加载 + 避免核心类篡改
.2. 加载机制
- 全盘负责: 当一个类加载器负责加载某个 Class 时,
该Class所依赖的和引用的其他Class也将由该类加载器负责载入
,除非显示使用另外一个类加载器来载入。 - 缓存机制: 缓存机制将会保证
所有加载过的Class都会被缓存
,当程序中需要使用某个 Class 时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区
。这就是为什么修改了 Class 后,必须重启 JVM,程序的修改才会生效。 - 双亲委派机制: 如果一个类加载器收到了类加载的请求,它
首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成
,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
.9. 对象创建
.10. 对象内存分布
.11. OutofMemory错误
.12. Full GC
.12. 拷贝
- 浅拷贝:如果属性是基本类型,拷贝的就是基本类型的值;如果
属性是内存地址
(引用类型),拷贝的就是内存地址 - 深拷贝:会拷贝所有的属性,并
拷贝属性指向的动态分配的内存
. 可以使用 Arrays.copyof ()
.13. 反射
- 是 JVM 在运行时动态加载类,调用方法或者访问属性,不需要知道运行对象是谁
- 相关的类:
- class: 每一个类都有一个 class 对象(类实例),包含类的相关信息
- Field:使用 get/set 修改或者读取 Field 相关对象
- method:使用
invoke方法调用与method相关联的对象
- Constructor: 使用 newInstance () 构建对象
- Object 类:Object 是所有 Java 类的父类。所有对象都默认实现了 Object 类的方法。
.14. 如何避免内存泄漏
- 未对作废数据内存单元置为 null
- 尽早释放无用对象的引用,
- 使用临时变量时,让引用变量在推出活动域后自动设置为 null,暗示垃圾收集器收集;
- 程序避免用 String 拼接,用 StringBuffer,因为每个 String 会占用内存一块区域;
- 尽量少用静态变量(全局不会回收);
- 不要集中创建对象尤其大对象,可以使用流操作;
- 尽量使用对象池,不再循环中创建对象,优化配置;
- 创建对象到单例 getInstance 中,对象无法回收被单例引用;
- 服务器 session 时间设置过长也会引起内存泄漏。
.15. 检查异常&运行时异常
-
检查异常是在程序中最经常碰到异常,
所有继承自 Exception 并且不是运行时异常的异常都是检查异常
,比如咱们最常见的 IO 异常和 SQL 异常。这种异常都发生在编译的阶段,Java 编译器强制程序去捕获此类型的异常,即它会把可能会出现这些异常的代码放到 try 块中,把对异常的处理代码放到 catch 块中。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。 -
运行时异常不同于检查异常,编译器没有强制对其进行捕获并处理,如果不对异常进行处理,那么当出现这种异常的时候,会由 JVM 来处理,比如 NullPointerException 异常,它就是运行时异常。只要程序设计得没有问题通常就不会发生运行时异常。