-
引用类型
- 强引用:类似Object o=new Object()这种引用。只要强引用还存在,对象不会被回收。
- 软引用:对于软引用关联的对象,在系统将要发生内存溢出前,会被纳入回收范围,并进行第二次回收。
- 弱引用:被弱引用关联的对象只能生存到下一次回收之前。无论内存是否足够都会被回收。
- 虚引用:不会影响对象生存时间,也无法获得对象实例,对其唯一期望是关联的实例在被回收时收到一个系统通知。
-
对象回收
-
引用计数法(Reference Counting)
- 给对象中添加一个引用计数器,被引用时+1,失效时-1。任何时刻引用计数为0则对象不可再使用。 这种算法很难应对循环引用的情况。
-
根搜索算法(GC Roots Tracing)
- 当一个对象到GC Roots不可达时,证明此对象是不可用的。根搜索算法中,无法到达的对象并非立即回收,而是被标记一次后进行筛选,筛选的条件是此对象是否有必要执行finaliz()方法。存在该方法(未被调用 过)的对象,将被放置在名为F-Queue的队列,并在稍后有虚拟机建立一个低优先级的Finalizer线程执行(虚拟机不承诺该方法执行结束,不一定 会执行), 稍后GC将对F-Queue进行第二次小规模标记,如果此时发现对象可达,将被移出即将回收集合。
-
可作为GC根的对象包括
- 虚拟机栈(中局部变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法中JNI(java本地方法)的引用对象。
-
-
方法区回收
- 废弃常量:与堆中实例回收类似(常量池中类、接口、方法、字段的符号引用也类似)。
- 无用的类:堆中没有该类实例——加载该类的Classloader已被回收,该类对应的java.lang.Class对象没有引用,无法通过反射访问。
- 大量使用反射、动态代理、CGLib等bytecode框架的场景,以及动态生成jsp或者osgi这类频繁自定义Classloader的场景都需要该功能
-
垃圾回收算法
-
标记-清除算法(Mark-Sweep)
- 算法:首先标记所有需要回收的对象,在标记完成后,统一回收所有被标记对象。
- 优点:不存在循环引用问题。
- 缺点:效率不高,会产生大量不连续内存碎片。
-
复制算法(Copying)
- 算法:将可用内存分为两块,每次只使用一块,回收时将此块内存中还生存的对象复制到另一块,把已使用的内存清理掉。
- 优点:不会出现碎片问题。
- 缺点:浪费。对象存活率较高时,效率降低。
-
标记-整理算法(Mark-Compact)
- 算法:与标记-清除算法类似,只是不直接回清除,而是让所有对象向一端移动,然后清理掉端边界以外的内存。
-
分代收集算法(Genrerational Collection)
- 算法:根据对象存活周期划分内存为几块,一般是把堆分为新生代和老年代,根据各个时代不同,采用不同算法。
-
-
垃圾收集器
-
示意图
-
-
-
Serial
- 单线程垃圾收集器,运行时停止其他线程直到GC结束。
- 新生代:复制算法。
- 老年代:标记-整理算法。
-
ParNew
- Serial的多线程版本。
- 新生代:复制算法。并行。
- 老年代:标记-整理算法。串行。
-
Parallel Scavenge
- 新生代收集器,使用复制算法。并行。类似ParNew收集器,此收集器更关注系统的吞吐量。可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也可以通过参数控制GC的时间不大于多少毫秒或者比例。
-
Serial Old
- 老年代收集器,使用标记-整理算法。串行。
-
Parallel Old
- Parallel Scavenge的老年代版本。使用标记-整理算法。并行。
-
Cms
- 以获取最短回收停顿时间为目标的收集器。标记-清除算法。
- 步骤:
- 初始标记:快速标记GC根能连接的对象。
- 并发标记:根搜索,标记待回收。
- 重新标记:应对标记期间变动的对象的标记记录。
- 并发清除:回收。
- CPU敏感。
- 无法处理浮动垃圾:一些在本次GC过程中生成的数据,只能留待下一次GC。如果GC过程中预留内存无法满足,会抛出Concurrent Mode Failure后,启动Serial Old进行老年代垃圾收集。
- 标记-清除算法会产生大量空间碎片,在无法为对象找到足够的连续空间时,会触发Full GC。
-
G1
- 基于标记-整理算法实现,可以精确控制停顿。
-
-
内存分配与回收策略
- 优先在eden分配。大对象直接进入老年代。长期存活对象将进入老年代。
时间: 2024-12-29 07:02:23