96SEO 2026-02-23 15:37 12
不同的垃圾回收器所对应的算法不一样效率更不一样。

在JDK8中默认为ParallelScavenge
new/old两者算法一致只不过ParallelScavenge
对于大大大大大大部分Java业务场景来说都是强引用基本上不会使用到软、弱、虚引用。
而在JDK1.2推出的软、弱、虚引用大部分出现场景都是在缓存中在JDK类库ThreadLocal、WeakHashMap。
框架Mybatis、Netty、以及各种缓存框架等等。
至于为什么要用在缓存中呢也很好理解因为这些引用实际上可有可无完美契合于缓存在有的时候给系统加速在系统内存紧张的时候清除缓存给核心业务使用。
这篇文章的篇幅会比较长也不容易理解。
因为对于软、弱、虚引用处理细节体现在Java层面和JVM层面恰好JVM层面又与GC垃圾回收细节强关联所以笔者只能竭尽所能
在Java层面就不得不补充一些前置知识以及Java层面如何处理这些引用。
上图是软、弱、虚引用最基本的表示这里需要区分2个不同的对象一个是软、弱、虚对象一个是软、弱、虚引用的对象。
所以下文需要介绍软、弱、虚对象的回收机制和区分具体的使用场景相信大家八股文多多少少背过这里跟八股文会有一点点出入
软当系统资源紧张但是又没那么那么紧张的时候根据最近最少使用回收软引用LRU算法当系统资源非常非常紧张的时候直接全部回收。
可以携带引用对象也可以使用ReferenceQueue去处理伴随对象
弱只要发生GC就会回收。
可以携带引用对象也可以使用ReferenceQueue去处理伴随对象
虚只要发生GC就会回收。
不能携带引用对象。
只能使用ReferenceQueue去处理伴随对象
上文有介绍软、弱、虚对象的回收机制这里有提到ReferenceQueue队列所以下文开始介绍Java层面如何使用ReferenceQueue做回收。
Handler);handler.setPriority(Thread.MAX_PRIORITY);
在java.lang.ref.Reference类中静态方法中创建了一个ReferenceHandler线程。
所以接下来看线程的执行体。
如果pedding不为null那么就代表GC回收到了软、弱、虚引用r
当还没产生pending链表的时候也即没有触发GC回收软、弱、虚引用//
当前线程直接去阻塞等待被JVM唤醒。
lock.wait();}return
把GC回收到了软、弱、虚引用放入到对应的ReferenceQueue中。
//
等待业务自己去处理ReferenceQueue队列。
ReferenceQueue?
r;queueLength;lock.notifyAll();return
是否为空如果为空代表当前GC没有触发回收软、弱、虚引用如果不为空代表当前GC回收软、弱、虚引用并且放入到pedding中把pedding的值放入到ReferenceQueue队列中业务维护的ReferenceQueue队列从队列中poll值去做对应的处理。
所以ReferenceQueue队列是业务层面自己维护传入到Reference中GC回收软、弱、虚引用后会把当前Reference放入到ReferenceQueue队列中。
业务层面再通过poll取到Reference做对应的处理可以是处理伴随对象
下面是WeakHashMap对ReferenceQueue的使用。
至此Java层面的处理已经看完接下来我们需要明白JVM是如何GC处理软、弱、虚引用并且放入到pedding中这样就全部闭环
/hotspot/src/share/vm/memory/genCollectedHeap.cpp
/hotspot/src/share/vm/memory/genCollectedHeap.cpp
GenCollectedHeap::do_collection(bool
||collector_policy()-should_clear_all_soft_refs();{…………for
从这里可以看出不同带都有一个引用的处理器。
ReferenceProcessor*
_gens[i]-ref_processor();rp-enable_discovery(true
改变回收策略rp-setup_policy(do_clear_all_soft_refs);
不同代进行垃圾回收。
_gens[i]-collect(full,
gc回收后把回收的软、弱、虚引用赋值给pedding交给Java层面处理//
{rp-enqueue_discovered_references();}
rp-set_enqueuing_is_done(false);}}}}…………}
这里根据策略决定是否要清理所有的软引用一般是内存资源极度不够的时候才会新生代或者老年代的垃圾回收器进行垃圾回收这也对应了YGC和FullGC在GC回收后把回收到的软、弱、虚引用赋值给pedding交给Java层面处理
所以接下来需要看到老年代的垃圾回收器进行垃圾回收的时候如何处理的软、弱、虚引用。
/hotspot/src/share/vm/memory/defNewGeneration.cpp
用于扫描软、弱、虚引用是否存活。
ScanWeakRefClosure
klass_scan_closure(fsc_with_no_gc_barrier,gch-rem_set()-klass_rem_set());//
Root直到找完所有的存活对象。
FastEvacuateFollowersClosure
this,fsc_with_no_gc_barrier,fsc_with_gc_barrier);//
Root复制到to区或者是老年代。
gch-gen_process_strong_roots(_level,true,
scavengingSharedHeap::ScanningOption(so),fsc_with_no_gc_barrier,true,
nmethodsfsc_with_gc_barrier,klass_scan_closure);//
因为这里是处理引用所以这里会处理软、弱、虚等等引用。
evacuate_followers.do_void();//
用于处理引用对象的存活。
FastKeepAliveClosure
scan_weak_ref);ReferenceProcessor*
根据clear_all_soft_refs这个bool字段决定是否清理全部的软引用。
rp-setup_policy(clear_all_soft_refs);//
rp-process_discovered_references(is_alive,
GC还是YGC都会对软、弱、虚引用做处理所以挑选YGC来做分析因为YGC简单一些但是对于软、弱、虚引用做处理都是一样的
由于处理软、弱、虚引用一定会和GC回收细节强关联所以很多是GC回收的细节代码笔者有吧注释给上并且当作黑盒就好。
创建好各种GC回收所需要扫描器这些扫描器最终都有一个共同的任务就是把存活对象复制到to区或者老年代GC
Root继续找引用直到遍历完整个堆软、弱、虚引的处理这也是接下来的重点
注意这里的软、弱、虚对象和软、弱、虚对象所引用对象是有区别的复制算法只会把软、弱、虚对象做复制软、弱、虚对象引用的对象要后续再做处理。
在看ReferenceProcessor类process_discovered_references方法之前需要介绍一下ReferenceProcessor类。
/hotspot/src/share/vm/memory/referenceProcessor.hpp
_default_soft_ref_policy;static
_always_clear_soft_ref_policy;ReferencePolicy*
可以很清楚的看到这里有策略对象和几个DiscoveredList链表。
链表中是保存了被处理的软、弱、虚的Java对象。
并且在遍历完所有的GC
所以接下来看到process_discovered_references方法具体处理细节。
/hotspot/src/share/vm/memory/referenceProcessor.cpp
ReferenceProcessor::process_discovered_references(BoolObjectClosure*
complete_gc,AbstractRefProcTaskExecutor*
java_lang_ref_SoftReference::clock();//
process_discovered_reflist(_discoveredSoftRefs,
时间戳用于LRU算法寻找最近最少使用的软引用。
update_soft_ref_master_clock();//
process_discovered_reflist(_discoveredWeakRefs,
process_discovered_reflist(_discoveredFinalRefs,
process_discovered_reflist(_discoveredPhantomRefs,
ReferenceProcessorStats(soft_count,
可以看到不管是软、弱、虚引用的处理都是调用process_discovered_reflist方法。
/hotspot/src/share/vm/memory/referenceProcessor.cpp
ReferenceProcessor::process_discovered_reflist(DiscoveredList
clear_referent,BoolObjectClosure*
complete_gc,AbstractRefProcTaskExecutor*
这个过滤是判断软、弱、虚对象引用的对象是否还活着如果活着那就不能处理这个引用。
for
根据clear_referent变量决定最终是否处理引用。
for
软引用才有策略根据策略决定是否回收对象如果策略不让回收的对象那么就需要从DiscoveredList链表中remove并且保持存活直到下次GC
再尝试回收经过策略的决策后活下来的对象继续做过滤这次过滤是判断软、弱、虚对象引用的对象是否还活着如果活着那就不能处理这个引用所以用不好随时可能内存泄漏如果引用对象是存活的那么就需要从DiscoveredList链表中remove并且保持存活直到下次GC
再尝试回收经过第二步的过滤活下来的对象还要根据clear_referent变量决定最终是否处理引用对象。
这一步只有虚引用才不能处理引用因为虚对象不能引用对象如果clear_reference为false那么就需要从DiscoveredList链表中remove并且保持存活直到下次GC
再尝试回收但是虚引用为false也没关系因为他指向本来就是null。
这里就比较简单了要不永远回收、要不永远不回收要不根据LRU算法得到最近最少使用的软引用优先回收没用的
所以在本文最上面写到软引用在内存紧张的时候但是不是非常紧张的时候会回收最少使用的根据LRU算法在内存非常非常紧张的时候策略直接是AlwaysCLearPolicy策略了就回收所有软引用
当经过层层过滤后最终存活的软、弱、虚对象就存在不同DiscoveredList链表中。
我们在Java层面是从pedding获取到对象所以这边还需要把不同的DiscoveredList链表设置到pedding中。
所以接下来回到GenCollectedHeap::do_collection方法看到enqueue_discovered_references方法
/hotspot/src/share/vm/memory/referenceProcessor.cpp
ReferenceProcessor::enqueue_discovered_references(AbstractRefProcTaskExecutor*
enqueue_discovered_ref_helperoop(this,
enqueue_discovered_ref_helper(ReferenceProcessor*
ref,AbstractRefProcTaskExecutor*
拿到Reference类中的pedding变量的地址因为pending是一个静态变量所以从mirror拿。
T*
(T*)java_lang_ref_Reference::pending_list_addr();//
把链表链到pedding上ref-enqueue_discovered_reflists((HeapWord*)pending_list_addr,
ReferenceProcessor::enqueue_discovered_reflists(HeapWord*
pending_list_addr,AbstractRefProcTaskExecutor*
只需要把每个链表的头部链到pending就行了。
enqueue_discovered_reflist(_discovered_refs[i],
pending_list_addr);_discovered_refs[i].set_head(NULL);_discovered_refs[i].set_length(0);}
这里就是把经过层层筛选的软、弱、虚链表中的对象链到Reference类中pedding字段上。
最终交给Java层面的ReferenceHandler线程去处理。
上面我们把所有的处理细节都分析完了所以接下来回忆到一处细节点。
/hotspot/src/share/vm/memory/referenceProcessor.cpp
文件中process_discovered_reflist方法这个方法是做过滤处理在process_phase2这个方法做过滤的时候会判断软、弱、虚对象的引用对象是否存活如果存活的情况下是不能做回收的。
所以这里很容易发生内存泄露看到如下的Java代码。
Object();weakHashMap.put(o1,new
只要o1不释放这就是内存泄露。
weakHashMap.put(1,new
1是JVM字符串常量池指向的所以这也是一个内存泄露byte[]
处理后的大小因为在size里面会去处理System.out.println(Reference
结果如上图所示发生GC后弱引用根本没有回收就是因为弱引用指向的对象被其他地方强引用导致于在做筛选的过程中被筛选出去了不能去回收它。
那么如果外部的这个强引用不释放那么这个弱引用引用的对象和弱引用对象永远无法回收从而无法达到弱引用的优势变相地说这就是内存泄漏
这里直接不让外部引用这个Object对象weakHashMap.put(new
处理后的大小因为在size里面会去处理System.out.println(Reference
如上图所示弱引用引用的对象不让外部有强引用后直接正常了发生GC就回收了
因为流程特别大强关联GC回收部分所以笔者只能竭尽所能源码注释总结画图来尽量描述明白
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback