没有合适的资源?快使用搜索试试~ 我知道了~
【JVM和性能优化】2.垃圾回收器和内存分配策略
0 下载量 96 浏览量
2020-12-22
06:40:11
上传
评论
收藏 2.68MB PDF 举报
温馨提示
试读
15页
文章目录内存回收引用计数法可达性分析浅谈引用强引用软引用弱引用虚引用方法区GC 算法标记-清除算法(Mark-Sweep)复制算法(Copying)标记-整理算法(Mark-Compact)GC算法综合用年轻代老年代永久代枚举根节点安全点安全区域GC回收器Serial 收集器ParNew 收集器Parallel Scavenge 收集器Serial Old 收集器Parallel Old 收集器CMS 收集器G1 收集器ZGCSTW实现内存分配与回收策略demo演示内存泄露跟内存溢出JDK提供自带工具参考 内存回收 为什么要了解GC(Garbage Collection)和内存分配策略 1、面
资源推荐
资源详情
资源评论
【【JVM和性能优化】和性能优化】2.垃圾回收器和内存分配策略垃圾回收器和内存分配策略
文章目录文章目录内存回收引用计数法可达性分析浅谈引用强引用软引用弱引用虚引用方法区GC 算法标记-清除算法(Mark-Sweep)复制算法(Copying)标记-整理算法(Mark-
Compact)GC算法综合用年轻代老年代永久代枚举根节点安全点安全区域GC回收器Serial 收集器ParNew 收集器Parallel Scavenge 收集器Serial Old 收集器Parallel Old 收集器CMS
收集器G1 收集器ZGCSTW实现内存分配与回收策略demo演示内存泄露跟内存溢出JDK提供自带工具参考
内存回收内存回收
为什么要了解GC(Garbage Collection)和内存分配策略
1、面试需要
2、GC对应用的性能是有影响的
3、写代码有好处
那些需要GC:
共享区的都要被回收比如堆区以及方法区。
在进行内存回收之前要做的事情就是判断那些对象是‘死’的,哪些是‘活’的。常用方法有两种 引用计数法引用计数法跟可达性分析可达性分析。
引用计数法引用计数法
给 Java 对象添加一个引用计数器,每当有一个地方引用它时,计数器 +1;引用失效则 -1,当计数器不为 0 时,判断该对象存活;否则判断为死亡(计数器 = 0)。
该方法的优点是实现简单,判断高效。缺点是无法解决 对象间相互循环引用 的问题,比如demo如下:
public class GcDemo {
public static void main(String[] args) {
GcObject object1 = new GcObject(); // step 1
GcObject object2 = new GcObject(); // step 2
object1.instance = object2 ;//step 3
object2.instance = object1; //step 4
object1 = null; //step 5
object2 = null; // step 6
}
}
class GcObject {
public Object instance = null;
}
step1: GcObject实例1的引用计数+1,实例1引用数 = 1
step2: GcObject实例2的引用计数+1,实例2引用数 = 1
step3: GcObject实例2的引用计数+1,实例2引用数 = 2
step4: GcObject实例1的引用计数+1,实例1引用数 = 2
step5: GcObject实例1的引用计数-1,结果为 1
step6: GcObject实例2的引用计数-1,结果为 1
至此发现实例1跟实例2的引用数都不为0而又相互引用。这两个实例所占有的内存则无法释放无法释放。
可达性分析可达性分析
很多主流商用语言(如Java、C#)都采用 引用链法 判断 Java对象是否存活,
将一系列的 GC Roots 对象作为起点,从这些起点开始向下搜索。
在Java语言中,可作为GC Roots的对象包含以下几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象。(可以理解为:引用栈帧中的本地变量表的所有对象)
方法区中静态属性引用的对象(可以理解为:引用方法区该静态属性的所有对象)
方法区中常量引用的对象(可以理解为:)引用方法区中常量的所有对象
本地方法栈中(Native方法)引用的对象(可以理解为:)
引用Native方法的所有对象
可以理解为:
首先第一种是虚拟机栈中的引用的对象,我们在程序中正常创建一个对象我们在程序中正常创建一个对象,对象会在堆上开辟一块空间,同时会将这块空间的地址作为引用保存到虚拟机栈中,如果对象生命周期结
束了,那么引用就会从虚拟机栈中出栈,因此如果在虚拟机栈中有引用,就说明这个对象还是有用的,这种情况是最常见的是最常见的。
第二种是我们在类中定义了全局的静态的对象全局的静态的对象,也就是使用了static关键字,由于虚拟机栈是线程私有的,所以这种对象的引用会保存在共有的方法区中,显然将方法区中的静态引
用作为GC Roots是必须的。
第三种便是常量引用,就是使用了static final关键字,由于这种引用初始化之后不会修改,所以方法区常量池里的引用的对象也应该作为GC Roots。最后一种是在使用JNI技术时,
有时候单纯的Java代码并不能满足我们的需求,我们可能需要在Java中调用C或C++的代码,因此会使用native方法,JVM内存中专门有一块本地方法栈,用来保存这些对象的引
用,所以本地方法栈中引用的对象也会被作为GC Roots。
含3个步骤:
可达性分析
第一次标记 & 筛选
第二次标记 & 筛选
可达性分析可达性分析
当一个对象到 GC Roots 没有任何引用链相连时,则判断该对象不可达。
注意: 可达性分析 仅仅只是判断对象是否可达是否可达,但还不足以判断对象是否存活 / 死亡
当在 可达性分析 中判断不可达的对象,只是“被判刑” = 还没真正死亡。
第一次标记和筛选第一次标记和筛选
对象 在 可达性分析中 被判断为不可达后,会被第一次标记 & 准备被筛选
不筛选:继续留在 ”即将回收“的集合里,等待回收;
筛选:从 ”即将回收“的集合取出
筛选的标准:该对象是否有必要执行 finalize()方法方法
若有必要执行(人为设置),则筛选出来,进入下一阶段(第二次标记若有必要执行(人为设置),则筛选出来,进入下一阶段(第二次标记 & 筛选);筛选);
若若没必要执行没必要执行,判断该对象死亡,不筛选 并等待回收.
当对象无finalize()方法 或finalize()已被虚拟机调用过,则视为“没必要执行”
第二次标记和筛选第二次标记和筛选
当对象经过了第一次的标记 & 筛选,会被进行第二次标记 & 准备被进行 筛选
a. 方式描述
该对象会被放到一个 F-Queue 队列中,并由 虚拟机自动建立、优先级低的Finalizer 线程去执行 队列中该对象的finalize()
finalize()只会被执行一次只会被执行一次
但并不承诺等待finalize()运行结束。这是为了防止 finalize()执行缓慢 / 停止 使得 F-Queue队列其他对象永久等待。
b. 筛选标准
在执行finalize()过程中,若对象依然没与引用链上的GC Roots 直接关联 或 间接关联(即关联上与GC Roots 关联的对象),那么该对象将被判断死亡,不筛选(留在”即将回收“集合
里) 并 等待回收.
整体流程如下:
浅谈引用浅谈引用
通过GC Root分析到到各种引用对象也有不同到引用级别。
强引用强引用
一般的Object obj = new Object() ,就属于强引用。
软引用软引用
SoftReference:一些有用但是并非必需,用软引用关联的对象,系统将要发生系统将要发生OOM之前,这些对象就会被回收之前,这些对象就会被回收,参见代码:
public class TestSoftRef {
public static class User {
public int id = 0;
public String name = "";
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
public static void main(String[] args) {
User u = new User(1, "sowhat");
SoftReference userSoft = new SoftReference(u);
u = null; // 保证new User(1,"sowhat") 这个实例只有userSoft在软引用
System.out.println(userSoft.get());
System.gc();//展示gc的时候,SoftReference不一定会被回收,只是建议回收
System.out.println("AfterGc");
System.out.println(userSoft.get()); // new User(1,"sowhat")没有被回收
List list = new LinkedList();
try {
for (int i = 0; i < 100; i++) {
//User(1,"sowhat")实例一直存在
System.out.println("********************" + userSoft.get());
list.add(new byte[1024 * 1024 * 1]);
}
} catch (Throwable e) {
//抛出了OOM异常后打印的,User(1,"sowhat")这个实例被回收了
System.out.println("Throwable********************" + userSoft.get());
}
}
}
弱引用弱引用
WeakReference :一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。发生时,不管内存够不够,都会被回收。
public class TestWeakRef {
public static class User{
public int id = 0;
public String name = "";
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
public static void main(String[] args) {
User u = new User(1,"sowhat");
WeakReference userWeak = new WeakReference(u);
u = null;
System.out.println(userWeak.get());
System.gc();
System.out.println("AfterGc");
System.out.println(userWeak.get());
}
}
虚引用虚引用
PhantomReference:幽灵引用,最弱,object.fun() 都无法执行 。 被垃圾回收的时候收到一个通知
方法区方法区
方法区一般存放类加载信息,字符串常量,静态变量或者静态不可变量等数据。回收静态常量跟无用类,静态常量一般就用引用可达法即可,但判断无用的类:要以下三个条件都满
足
该类所有的实例都已经回收,也就是 Java 堆中不存在该类的任何实例
加载该类的 ClassLoader 已经被回收
该类对应的 java.lang.Class 对象没有任何地方呗引用,无法在任何地方通过反射访问该类的方法
GC 算法算法
标记标记-清除算法(清除算法(Mark-Sweep))
算法分为标记标记和清除清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
它的主要不足空间问题,标记清除之后会产生大量不连续的内存碎片不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前不得不提前
触发另一次垃圾收集动作。触发另一次垃圾收集动作。
剩余14页未读,继续阅读
资源评论
weixin_38709100
- 粉丝: 4
- 资源: 959
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功