Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 📝 Notes
    • Algorithm
      • Algorithm - Bloom Filter
      • Algorithm - Disjoint Set
      • Algorithm - Fast Power
      • Algorithm - KMP
      • Algorithm - Monotonic Stack
      • Algorithm - RB-Tree
      • Algorithm - Regular Expression
      • Algorithm - Sliding Window
      • Online Judge - I/O
    • C++
      • C++ - Const
      • C++ File I/O
      • C++ - Object Layout
      • C++ - Operator Overload
      • C++ - Polymorphism
      • C++ STL algorithm
      • C++ STL map
      • C++ STL multimap
      • C++ STL priority_queue
      • C++ STL set
      • C++ STL string
      • C++ STL unordered_map
      • C++ STL vector
      • C++ - Smart Pointer
      • C++ - Template & Genericity
    • Compiler
      • ANTLR - Basic
      • Compiler - LLVM Architecture
      • Compiler - Multi-version GCC
    • Cryptography
      • Cryptography - Certbot
      • Cryptography - Digital Signature & PKCS #7
      • Cryptography - GPG
      • Cryptography - JWT
      • Cryptography - Keystore & Certificates
      • Cryptography - OAuth 2.0
      • Cryptography - Java 实现对称与非对称加密算法
      • Cryptography - TLS
    • DevOps
      • DevOps - Travis CI
    • Docker
      • Docker - Image & Storage Management
      • Docker - Image
      • Docker - Libcontainer
      • Docker - Multi-Arch Image
      • Docker - Multi-Stage Build
      • Docker - Network
      • Docker - Orchestration & Deployment
      • Docker - Overview
      • Docker - Service Building
      • Docker - Volume & Network Usage
      • Docker - Volume
      • Linux - Control Group
      • Linux - Namespace
    • Git
      • Git - Branch & Merge
      • Git - Cached
      • Git - Cherry Pick
      • Git - Commit
      • Git - Patch
      • Git - Proxy
      • Git - Rebase
      • Git - Reset
      • Git - Stash
      • Git - Theme for Git-Bash
    • Java
      • JVM - Synchronized
      • JVM - Volatile
      • Java - Annotation 注解
      • Java - BIO & NIO
      • Java - Class Path
      • Java - Condition and LockSupport
      • Java - Current Timestamp
      • Java - Deep Copy
      • Java - 运行环境配置
      • Java - Equals
      • Java - Exporting JAR
      • Java - Javadoc
      • Java - Lock
      • Java - Maven 项目构建工具
      • Java - References
      • Java - Reflection Mechanism
      • Java - String Split
      • Java - Thread Pool
      • Java - Thread
      • Tomcat - Class Loader
      • Tomcat - Container
    • Linux
      • addr2line
      • cut
      • df
      • du
      • fallocate
      • find
      • fio
      • grep
      • groupadd
      • gzip
      • head / tail
      • hexdump
      • iostat
      • iotop
      • kill
      • ldd
      • lsof
      • ltrace / strace
      • mpstat
      • netstat
      • nm
      • pidstat
      • pmap
      • readlink
      • readlink
      • rpm2cpio / rpm2archive
      • sort
      • tee
      • uniq
      • useradd
      • usermod
      • watch
      • wc
      • which
      • xargs
    • MS Office
      • MS Office - Add-in Dev
      • MS Office - Application
    • MySQL
      • InnoDB - Architecture
      • InnoDB - Backup
      • InnoDB - Checkpoint
      • InnoDB - Critical Features
      • InnoDB - Files
      • InnoDB - Index
      • InnoDB - Insert Buffer
      • InnoDB - Lock
      • InnoDB - Partition Table
      • InnoDB - Table Storage
      • MySQL - Server Configuration
      • MySQL - Storage Engine
    • Network
      • Network - ARP
      • Network - FTP
      • Network - GitHub Accelerating
      • HTTP - Message Format
      • HTTP - POST 提交表单的两种方式
      • Network - Proxy Server
      • Network - SCP
      • Network - SSH
      • Network - TCP Congestion Control
      • Network - TCP Connection Management
      • Network - TCP Flow Control
      • Network - TCP Retransmission
      • Network - Traceroute
      • Network - V2Ray
      • Network - WebSocket
      • Network - Windows 10 Mail APP
      • Network - frp
    • Operating System
      • Linux - Kernel Compilation
      • Linux - Multi-OS
      • Linux - Mutex & Condition
      • Linux - Operations
      • Linux: Package Manager
      • Linux - Process Manipulation
      • Linux - User ID
      • Linux - Execve
      • OS - Compile and Link
      • OS - Dynamic Linking
      • OS - ELF
      • Linux - Image
      • OS - Loading
      • OS - Shared Library Organization
      • OS - Static Linking
      • Syzkaller - Architecture
      • Syzkaller - Description Syntax
      • Syzkaller - Usage
      • Ubuntu - Desktop Recover (Python)
      • WSL: CentOS 8
    • Performance
      • Linux Performance - Perf Event
      • Linux Performance - Perf Record
      • Linux Performance - Perf Report
      • Linux Performance - Flame Graphs
      • Linux Performance - Off CPU Analyze
    • PostgreSQL
      • PostgreSQL - ANALYZE
      • PostgreSQL - Atomics
      • PostgreSQL - CREATE INDEX CONCURRENTLY
      • PostgreSQL - COPY FROM
      • PostgreSQL - COPY TO
      • PostgreSQL - Executor: Append
      • PostgreSQL - Executor: Group
      • PostgreSQL - Executor: Limit
      • PostgreSQL - Executor: Material
      • PostgreSQL - Executor: Nest Loop Join
      • PostgreSQL - Executor: Result
      • PostgreSQL - Executor: Sequential Scan
      • PostgreSQL - Executor: Sort
      • PostgreSQL - Executor: Unique
      • PostgreSQL - FDW Asynchronous Execution
      • PostgreSQL - GUC
      • PostgreSQL - Locking
      • PostgreSQL - LWLock
      • PostgreSQL - Multi Insert
      • PostgreSQL - Plan Hint GUC
      • PostgreSQL - Process Activity
      • PostgreSQL - Query Execution
      • PostgreSQL - Spinlock
      • PostgreSQL - Storage Management
      • PostgreSQL - VFD
      • PostgreSQL - WAL Insert
      • PostgreSQL - WAL Prefetch
    • Productivity
      • LaTeX
      • Venn Diagram
      • VuePress
    • Solidity
      • Solidity - ABI Specification
      • Solidity - Contracts
      • Solidity - Expressions and Control Structures
      • Solidity - Layout and Structure
      • Solidity - Remix IDE
      • Solidity - Slither
      • Solidity - Types
      • Solidity - Units and Globally Available Variables
    • Vue.js
      • Vue.js - Environment Variable
    • Web
      • Web - CORS
      • Web - OpenAPI Specification
    • Wireless
      • Wireless - WEP Cracking by Aircrack-ng
      • Wireless - WPS Cracking by Reaver
      • Wireless - wifiphisher

Java - References

Created by : Mr Dk.

2020 / 11 / 10 17:33

Nanjing, Jiangsu, China


理解并实验了 Java 中几种不同类型的引用及其特性。该问题来源于研究 JDK 源代码的 ThreadLocal 类时,其中的 ThreadLocalMap 使用的是所谓 弱引用。在 马士兵 老师的 多线程与高并发 书籍中找到了答案。

Strong References

强引用就是默认的引用类型,任何被强引用的对象都不会被 GC,除非强引用变量被重新指向 null。做一个简单的小型实验:创建一个测试类,重写该类的 finalize() 函数。

Java 会在 GC 对象时自动调用 finalize() 函数,重写该函数仅为观察对象的时机。实际上,该函数永远不用也不应该被重写。

class TestReference {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("GC happened.");
    }
}
TestReference r = new TestReference();
System.out.println("First GC trial.");
System.gc();
r = null;
System.out.println("Second GC trial.");
System.gc();
System.in.read(); // GC 在其它线程中进行,防止当前线程结束

得到的运行结果如下。可以看到,强引用只有在赋值为 null 后才会发生 GC。

First GC trial.
Second GC trial.
GC happened.

Soft References

软引用需要被显式使用。对于被软引用的对象来说,JVM 在内存充裕时不会急着 GC 它;只有当 JVM 急需内存时,才会回收被软引用的对象。可以通过调低 JVM 的堆内存来测试:

SoftReference<TestReference> r = new SoftReference<>(new TestReference());
System.gc();
System.out.println(r.get()); // maybe not null
// ... fulfill JVM's memory
System.out.println(r.get()); // maybe null
System.in.read(); // GC 在其它线程中进行,防止当前线程结束

这个特性可以用于实现 缓存 - 如果内存够用,就保留对象用于提升性能;如果内存不够用,就回收缓存保证 JVM 有足够的内存正常运行。

Weak References

弱引用需要被显式使用。如果 JVM 检测到一个对象 只被弱引用 (没有任何强引用或软引用),那么将会 GC 该对象。

WeakReference<TestReference> r = new WeakReference<>(new TestReference());
System.out.println(r.get()); // not null
System.gc(); // GC
System.out.println(r.get()); // null
System.in.read(); // GC 在其它线程中进行,防止当前线程结束

这一特性主要体现在 Java 的 ThreadLocal 类中。该类在当前线程对象内维护一个称为 ThreadLocalMap 的 map,里面的 key 是弱引用的 ThreadLocal 对象,value 是想要保存在线程内部保存的局部变量。当线程用强引用实例化一个 ThreadLocal 对象并作为某个局部变量的 key 放入线程 map 中时,此时堆上的 ThreadLocal 对象被引用两次:

  • 程序中的强引用
  • 线程内的 ThreadLocalMap 弱引用

当程序中强引用所在的函数结束后,该引用作为函数内的局部变量 (栈上变量) 也消失了。此时,如果线程一直运行下去 (假设它是一个后台线程),map 将一直对堆上的 ThreadLocal 对象保持引用。假设 map 持有的是强引用,那么 ThreadLocal 对象将一直不会被 GC,从而引发内存泄露;而如果这是个弱引用,那么堆上的 ThreadLocal 对象将会在原有强引用断开连接后被 GC。

一个例子。在这里,我实现了自己的 ThreadLocal 类,主要是重写了 finalize() 函数以观察 ThreadLocal 对象被 GC 的时机:

class MyThreadLocal<T> extends ThreadLocal<T> {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Thread local object GC happened.");
    }
}

class TestReference {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("Value object GC happened.");
    }
}

执行以下代码。将 ThreadLocal 对象设置到 ThreadLocalMap 中后,断开程序中对它的强引用,只剩下 ThreadLocalMap 中的弱引用,看看 ThreadLocal 对象会不会被 GC。

MyThreadLocal<TestReference> threadLocal = new MyThreadLocal<>();
TestReference r = new TestReference();
threadLocal.set(r);
r = null; // No strong reference to value anymore.
System.out.println("First GC trial.");
System.gc();
threadLocal = null; // No strong reference to thread local anymore.
System.out.println("Second GC trial.");
System.gc();
System.in.read();

得到的运行结果,ThreadLocal 对象被 GC 了。

First GC trial.
Second GC trial.
Thread local object GC happened.

这里有件很蛋疼的事:虽然 map 中的 key 引用的堆上对象 (ThreadLocal) 已经被 GC 了而成为了 null,但是 value 对象并没有被 GC,依旧被 map 强引用 - 这样也会产生内存泄漏。ThreadLocalMap 的实现中其实已经考虑到了这个问题,在调用成员函数 set() / get() / remove() 时,会顺便对 map 中所有 key 为 null 的 value 进行清理。然而,极端情况下,如果这几个函数之后一直没有被调用,那么内存泄漏实际上一直存在。我们还是应当要有把任何不合理的事情扼杀在摇篮里的基本修养。当不再显式使用一个 ThreadLocal 对象时,主动调用 remove() 将其从当前线程的 map 中清除掉:

MyThreadLocal<TestReference> threadLocal = new MyThreadLocal<>();
TestReference r = new TestReference();
threadLocal.set(r);
r = null; // No strong reference to value anymore.
System.out.println("First GC trial.");
System.gc();
threadLocal.remove(); // Remove the value from the thread local map.
threadLocal = null; // No strong reference to thread local anymore.
System.out.println("Second GC trial.");
System.gc();
System.in.read();

得到结果如下:

First GC trial.
Second GC trial.
Thread local object GC happened.
Value object GC happened.

Phantom References

虚引用的构造函数至少需要两个参数:除了被引用的对象外,另一个参数是一个 引用队列。当引用的对象要被 GC 时 (finalize() 被调用后),JVM 将会把引用放进队列中。如果在这个队列中检测到了变动,说明对象被 GC 了。对虚引用调用 get() 是取不到值的。

写不出实验代码了。虚引用主要用于管理堆外内存,也就是不在 GC 管理范围以内的内存。当 GC 发生后,通过检测引用队列,相当于可以通知程序显式释放堆外内存。

References

马士兵 - 多线程与高并发 (2020 年第一版)

GeeksforGeeks - Types of References in Java

CSDN - ThreadLocalMap 里的弱引用


Edit this page on GitHub
Prev
Java - Maven 项目构建工具
Next
Java - Reflection Mechanism