java數據類型詳解篇

1、8種基本數據類型

數據類型分類字節數內存位數是否最高位為符號位
(0正數1負數)
取值范圍(數值形式)取值說明
byte整數類型18-128 ~ 127-2^7 ~ 2^7 - 1 (冪形式)
short整數類型216-32,768 ~ 32,767-2^15 ~ 2^15 - 1 (冪形式)
int整數類型432-2,147,483,648 ~ 2,147,483,647-2^31 ~ 2^31 - 1 (冪形式)
long整數類型864-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807-2^63 ~ 2^63 -1 (冪形式)
float浮點類型(單精度)432正數:1.4e-45 ~ 3.402e38
負數:-3.4028235e38 ~ -1.4e-45
6~7位有效十進制小數位
double浮點類型(雙精度)864正數:4.9e-324 ~ 1.797e308
負數:-1.797e308 ~ 4.9e-324
15位十進制小數位
charUnicode字符類型2160 ~ 65,5350 ~ 2^16 -1
與 Unicode 編碼的直接對應,如下
char c = 65;
// 輸出的不是65而是A字符 System.out.println?;
boolean布爾類型無固定無固定無固定無固定,實際占用取決于 JVM 實現和使用場景

根據JVM的內存模型,基本數據類型存儲位置取決于聲明位置和使用方式:

  • 方法內部聲明,則存儲在棧里
  • 在實例類中聲明,則存儲在堆里

2、引用數據類型

1、自定義類或java api提供的類

存儲對象的引用(內存地址),指向實際堆存儲的位置。

Date now = new Date();       // 日期類
Person p = new Person();     // 自定義類實例
class Person {String name;int age;
}

字符串String 特別說明:

String有緩存機制,主要通過 字符串常量池(String Pool) 實現,相同內容的字符串只存儲一份,后續重復使用直接引用池中的對象。

字面量賦值(自動入池)

String s1 = "Fly";       // 第一次創建,存入常量池
String s2 = "Fly";       // 直接復用常量池中的對象
System.out.println(s1 == s2); // true(地址相同)

顯式調用 intern()(手動入池)

String s3 = new String("Fly"); // 在堆中創建新對象
String s4 = s3.intern();       // 將s3內容加入常量池(若池中已有則返回引用)
System.out.println(s1 == s4);  // true(s4指向常量池對象)

new String() 不觸發自動緩存

String s5 = new String("Fly"); // 強制在堆中創建新對象
String s6 = new String("Fly"); // 另一個新對象
System.out.println(s5 == s6);  // false(地址不同)
System.out.println(s1 == s5);  // false(常量池 vs 堆新對象)

注意:運行時拼接(不觸發緩存)

// 示例1:編譯期優化(字面量拼接)
String a = "Fly" + "Fish";  // 編譯后自動合并為 "FlyFish",復用常量池對象// 示例2:運行時拼接(不觸發緩存)// 循環內拼接字符串用 StringBuilder,避免生成大量中間對象
String b = "Fly";
String c = b + "Fish";      // 運行時在堆中生成新對象
String d = "FlyFish";
System.out.println(a == d);  // true(a、d在常量池)
System.out.println(c == d);  // false(c在堆,d在常量池)

使用緩存減少對象創建開銷,加速字符串比較(==equals() 快),緩存機制下 == 有時有效,但非字面量字符串比較必須用 equals()

在java8之前內部使用 char[](字符數組)存儲數據,java9及之后為節省內存,改為 byte[] + 編碼標志(Latin-1 或 UTF-16),但邏輯上仍等價于字符序列。

2、接口類

存儲對象的引用(內存地址),指向實際堆存儲的位置。

interface Drawable {void draw();
}class Circle implements Drawable {public void draw() {System.out.println("Drawing circle");}
}Drawable d = new Circle();  // 接口引用指向實現類

3、數組類型

數組如果是引用對象存儲對象的引用(內存地址),指向實際堆存儲的位置。

數組如果是基本數據類型,則按基本數據類型存儲在一致。

int[] numbers = {1, 2, 3};             // 基本類型數組
String[] names = new String[5];         // 引用類型數組
int[][] matrix = {{1,2}, {3,4}};        // 多維數組

4、枚舉類型

引用對象存儲對象的引用(內存地址),指向實際堆存儲的位置。

enum Color {RED, GREEN, BLUE
}
Color c = Color.RED;  // 枚舉引用

5、注解類型

引用對象存儲對象的引用(內存地址),指向實際堆存儲的位置。

@Retention(RetentionPolicy.RUNTIME)
@interface Author {String name();int version() default 1;
}@Author(name = "John")
class MyClass {...}

6、集合框架

List<String> list = new ArrayList<>();  // 有序列表
Set<Integer> set = new HashSet<>();     // 唯一值集合
Map<String, Integer> map = new HashMap<>(); // 鍵值對映射

對象存儲對象的引用(內存地址),指向實際堆存儲的位置。

7、8中基本數據類型對應的包裝類

基本類型包裝類自動裝箱/拆箱值緩存比較陷阱(建議始終用equals比較包裝對象)
byteByte類似-128 ~ 127Integer x = 100, y = 100;
System.out.println(x == y); // true(緩存內)

Integer m = 200, n = 200;
System.out.println(m == n); // false(緩存外)
shortShort類似-128 ~ 127類似Integer
intIntegerInteger num = 42; // 自動裝箱 (int → Integer)
int value = num; // 自動拆箱 (Integer → int)
-128 ~ 127類似Integer
longLong類似-128 ~ 127類似Integer
floatFloat自動裝箱都創建新對象。無緩存
doubleDouble自動裝箱都創建新對象。無緩存
charCharacter類似** 0 ~ 127**(ASCII字符范圍)類似Integer
booleanBoolean僅有兩個靜態實例Boolean.TRUEBoolean.FALSE,所有自動裝箱均復用它們。

3、四大引用類型

GC回收時機
永不回收
內存不足時回收
下次GC時回收
回收后通知
強引用
對象
軟引用
弱引用
虛引用
一、強引用
說明

強引用是 Java 中最常見、最默認的引用類型。只要一個對象被至少一個強引用指向,垃圾收集器 (GC) 就絕對不會回收它。只有當所有指向該對象的強引用都斷開(被設置為 null 或超出作用域)后,該對象才會變得可回收

使用場景舉例
    public class StrongReferenceExample {public static void main(String[] args) {// 1. 創建一個新的 Person 對象 "Alice"//    alice 是一個強引用,指向堆內存中創建的 Person("Alice") 對象Person alice = new Person("Alice"); // 強引用建立!System.out.println("Alice created: " + alice.getName()); // 輸出: Alice created: Alice// 2. 讓 alice 引用指向一個 *新* 的 Person 對象 "Bob"//    現在,第一個 Person("Alice") 對象失去了它唯一的強引用 (alice 不再指向它)alice = new Person("Bob"); // 強引用指向了新對象,"Alice" 的強引用斷開!System.out.println("Now Alice refers to: " + alice.getName()); // 輸出: Now Alice refers to: Bob// 3. 顯式觸發垃圾收集 (注意:這只是建議,GC 不保證立即執行)for (int i = 0; i < 3; i++) {System.gc();}// 4. 給 GC 一點時間運行 (實際應用中通常不需要這樣),GC回收后日志即可看到:!!! GC is collecting Person: Alice !!!try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Program finished.");}}class Person {private String name;public Person(String name) {this.name = name;System.out.println("Person constructor: " + name + " created in memory.");}public String getName() {return name;}// 重點:覆寫 finalize 方法 (不推薦用于生產,僅用于演示GC行為)@Overrideprotected void finalize() throws Throwable {System.out.println("!!! GC is collecting Person: " + name + " !!!");super.finalize();}}
二、軟引用
說明

通過SoftReference類創建,只有當內存不足時,才會被回收,所以比弱引用的生命周期更長點,適合實現內存敏感的緩存,如圖片緩存等。這些緩存對象在內存充足時可以提升性能,但在內存緊張時會被自動回收,為更重要的對象騰出空間,避免 OOM

JVM 不會一次性回收所有軟引用對象。回收過程是有策略、漸進式的,具體行為取決于 JVM 的實現和垃圾回收算法。

  • 按需回收:只回收足夠多的軟引用對象來緩解當前內存壓力,而非一次性全部回收。
  • 策略性回收:JVM 可能根據內部策略(如最近最少使用 LRU)選擇性地回收部分軟引用對象。如HotSpot 虛擬機使用類似 LRU(Least Recently Used)的策略,在每個軟引用對象關聯一個“時間戳”,記錄最近一次被訪問(通過 get() 方法)的時間。內存不足時,優先回收最久未被使用的軟引用對象。回收會持續進行,直到釋放的內存滿足需求或沒有更多可回收的軟引用。不同 JVM 版本或供應商(如 OpenJDK、Oracle JDK)的實現策略可能不同。
  • JVM的垃圾回收算法:軟引用對象可能分布在新生代(Young Gen)或老年代(Old Gen)。觸發 Full GC(回收整個堆)時,才會全面掃描并回收老年代中的軟引用。如果 Minor GC(回收新生代)后內存仍然不足,可能直接觸發 Full GC 來回收老年代的軟引用。
使用場景舉例

適合實現內存敏感的緩存,如圖片緩存等。這些緩存對象在內存充足時可以提升性能,但在內存緊張時會被自動回收,為更重要的對象騰出空間,避免 OOM

/*** 軟引用例子*/
public class SoftReferenceExample {// 核心緩存結構:文件路徑 -> 文件內容的軟引用private final Map<String, SoftReference<String>> cache = new HashMap<>();// 引用隊列,用于跟蹤哪些軟引用已被GC回收(內容已被清除)private final ReferenceQueue<String> refQueue = new ReferenceQueue<>();// 從緩存獲取文件內容(如果存在且未被回收),否則從磁盤讀取并緩存public String getFileContent(String filePath) {// 1. 清理已被GC回收的緩存條目evictCollectedEntries();// 2. 嘗試從緩存中獲取軟引用SoftReference<String> softRef = cache.get(filePath);// 3. 如果軟引用存在,嘗試獲取其引用的實際內容String content = null;if (softRef != null) {content = softRef.get(); // get() 方法獲取被引用的對象,如果已被GC則返回null}// 4. 如果從軟引用成功獲取到內容(content != null),直接返回緩存內容if (content != null) {System.out.println("Retrieved from cache: " + filePath);return content;}// 5. 緩存未命中(軟引用不存在,或軟引用存在但其內容已被GC回收)System.out.println("Cache miss (or collected). Reading from disk: " + filePath);// 模擬從磁盤讀取文件內容 (實際應用中替換為真實IO操作)content = readFileFromDisk(filePath);// 6. 將新讀取的內容用軟引用包裝,并與文件路徑關聯放入緩存//    同時注冊引用隊列,以便后續知道該引用何時被回收softRef = new SoftReference<>(content, refQueue);cache.put(filePath, softRef);return content;}// 清理那些已經被垃圾回收器回收了內容的軟引用條目private void evictCollectedEntries() {SoftReference<? extends String> clearedRef;// 從引用隊列中取出所有已被GC回收的軟引用while ((clearedRef = (SoftReference<? extends String>) refQueue.poll()) != null) {// 遍歷緩存,找到這個被回收的軟引用對應的條目并移除// (注意:這里簡單遍歷,實際高效實現可能需要反向映射或其他結構)SoftReference<? extends String> finalClearedRef = clearedRef;cache.entrySet().removeIf(entry -> entry.getValue() == finalClearedRef);System.out.println("Evicted collected reference from cache.");}}// 模擬從磁盤讀取文件(簡單返回一個模擬的大字符串)private String readFileFromDisk(String filePath) {// 模擬讀取大文件:創建一個較大的字符串StringBuilder sb = new StringBuilder();sb.append("Content of file: ").append(filePath).append("\n");for (int i = 0; i < 10000; i++) { // 增加字符串大小以模擬大文件sb.append("This is line ").append(i).append(" in the file.\n");}return sb.toString();}// 測試主方法public static void main(String[] args) {SoftReferenceExample fileCache = new SoftReferenceExample();// 第一次讀取文件A - 會從磁盤讀取并緩存String contentA1 = fileCache.getFileContent("fileA.txt");System.out.println("Content A length: " + contentA1.length());// 第二次讀取文件A - 應該從緩存命中String contentA2 = fileCache.getFileContent("fileA.txt");System.out.println("Content A length (again): " + contentA2.length());// 模擬內存壓力:嘗試緩存多個大文件System.out.println("\nSimulating memory pressure by caching many files...");for (int i = 0; i < 100; i++) {fileCache.getFileContent("large_file_" + i + ".dat");}System.out.println("Cached many large files.");// 嘗試再次讀取文件A - 可能命中,也可能被回收了需要重新讀取System.out.println("\nTrying to access fileA.txt again...");String contentA3 = fileCache.getFileContent("fileA.txt");System.out.println("Content A length (after pressure): " + (contentA3 != null ? contentA3.length() : "null"));}
}
三、弱引用
說明

通過WeakReference類創建的,下次GC時無論內存是否充足,只要對象僅被弱引用指向(沒有強引用或軟引用),無論內存是否充足,該對象都會被回收。

使用場景舉例

ThreadLocal內部實現的key即ThreadLocal本身就是用的弱引用,具體查看文章 java線程變量ThreadLocal用法篇

通常用在臨時緩存的場景使用,WeakHashMap舉例如下

static class Config {private final String value;private final byte[] payload; // 增加內存占用public Config(String value) {this.value = value;this.payload = new byte[12048]; // 每個配置對象占用2KB}}/*** 實例本身不可回收,生命周期與類加載器綁定,當鍵失去所有外部強引用時,GC時即可回收*/private static final WeakHashMap<Object, Config> cache = new WeakHashMap<>();private static final WeakHashMap<Object, Config> cache2 = new WeakHashMap<>();// 內存監控工具private static final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();// 打印內存使用情況private static void printMemoryStats(String label) {// 獲取堆內存使用情況MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();long usedHeap = heapUsage.getUsed() / 1024; // KB// 獲取非堆內存使用情況MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage();long usedNonHeap = nonHeapUsage.getUsed() / 1024; // KBSystem.out.printf("[%s] 堆內存: %d KB | 非堆內存: %d KB | cache大小: %d | cache2大小: %d%n",label, usedHeap, usedNonHeap, cache.size(), cache2.size());}/*** 這種方式字符串在常量池中,所以key存在強引用,所以key不會被回收*/static void testStringLiteral() {System.out.println("\n===== 開始測試字符串字面量 =====");printMemoryStats("測試前");String key1 = "key1";String key2 = "key2";String key3 = "key3";String key4 = "key4";String key5 = "key5";String key6 = "key6";String key7 = "key7";String key8 = "key8";String key9 = "key9";String key10 = "key10";cache.putIfAbsent(key1, new Config("value_key1")); // 1KBcache.putIfAbsent(key2, new Config("value_key2")); // 1KBprintMemoryStats("添加中 " + 2);cache.putIfAbsent(key3, new Config("value_key3")); // 1KBcache.putIfAbsent(key4, new Config("value_key4")); // 1KBprintMemoryStats("添加中 " + 4);cache.putIfAbsent(key5, new Config("value_key5")); // 1KBcache.putIfAbsent(key6, new Config("value_key6")); // 1KBprintMemoryStats("添加中 " + 6);cache.putIfAbsent(key7, new Config("value_key7")); // 1KBcache.putIfAbsent(key8, new Config("value_key8")); // 1KBprintMemoryStats("添加中 " + 8);cache.putIfAbsent(key9, new Config("value_key9")); // 1KBcache.putIfAbsent(key10, new Config("value_key10")); // 1KBprintMemoryStats("添加中 " + 10);printMemoryStats("添加后");System.out.println("字面量測試完成");}/*** 使用 new String()在堆中創建的新對象,不存在強引用所以key可以回收,value值對象將失去來自 Map 的強引用* 沒有引用的value對象即可被GC回收。* 正常業務使用具體對象*/static void testNewString() {System.out.println("\n===== 開始測試 new String() =====");printMemoryStats("測試前");for (int i = 0; i < 10; i++) {// 使用 new String() - 可回收cache2.putIfAbsent(new String("key" + i), new Config("value"+i)); // 1KB// 每1000次打印一次內存if (i % 2 == 0) {printMemoryStats("添加中 " + i);}}printMemoryStats("添加后");System.out.println("new String() 測試完成");}/*** 執行GC并等待清理完成*/private static void forceGCAndWait() {System.out.println("\n觸發GC...");printMemoryStats("GC前");// 多次觸發GC確保執行for (int i = 0; i < 3; i++) {System.gc();try {Thread.sleep(200); // 給GC時間執行} catch (InterruptedException e) {Thread.currentThread().interrupt();}}printMemoryStats("GC后");}/**** [程序啟動] 堆內存: 4096 KB | 非堆內存: 4038 KB | cache大小: 0 | cache2大小: 0** ===== 開始測試字符串字面量 =====* [測試前] 堆內存: 4096 KB | 非堆內存: 4229 KB | cache大小: 0 | cache2大小: 0* [添加中 2] 堆內存: 4096 KB | 非堆內存: 4234 KB | cache大小: 2 | cache2大小: 0* [添加中 4] 堆內存: 4096 KB | 非堆內存: 4238 KB | cache大小: 4 | cache2大小: 0* [添加中 6] 堆內存: 4096 KB | 非堆內存: 4239 KB | cache大小: 6 | cache2大小: 0* [添加中 8] 堆內存: 4096 KB | 非堆內存: 4244 KB | cache大小: 8 | cache2大小: 0* [添加中 10] 堆內存: 4096 KB | 非堆內存: 4246 KB | cache大小: 10 | cache2大小: 0* [添加后] 堆內存: 4096 KB | 非堆內存: 4246 KB | cache大小: 10 | cache2大小: 0* 字面量測試完成** 觸發GC...* [GC前] 堆內存: 4096 KB | 非堆內存: 4247 KB | cache大小: 10 | cache2大小: 0* [GC后] 堆內存: 4076 KB | 非堆內存: 4250 KB | cache大小: 10 | cache2大小: 0** ===== 開始測試 new String() =====* [測試前] 堆內存: 4076 KB | 非堆內存: 4254 KB | cache大小: 10 | cache2大小: 0* [添加中 0] 堆內存: 4076 KB | 非堆內存: 4402 KB | cache大小: 10 | cache2大小: 1* [添加中 2] 堆內存: 4076 KB | 非堆內存: 4408 KB | cache大小: 10 | cache2大小: 3* [添加中 4] 堆內存: 4076 KB | 非堆內存: 4410 KB | cache大小: 10 | cache2大小: 5* [添加中 6] 堆內存: 4076 KB | 非堆內存: 4411 KB | cache大小: 10 | cache2大小: 7* [添加中 8] 堆內存: 4076 KB | 非堆內存: 4413 KB | cache大小: 10 | cache2大小: 9* [添加后] 堆內存: 4076 KB | 非堆內存: 4416 KB | cache大小: 10 | cache2大小: 10* new String() 測試完成** 觸發GC...* [GC前] 堆內存: 4076 KB | 非堆內存: 4420 KB | cache大小: 10 | cache2大小: 10* [GC后] 堆內存: 4956 KB | 非堆內存: 4899 KB | cache大小: 10 | cache2大小: 0** ===== 最終內存報告 =====* [最終狀態] 堆內存: 4956 KB | 非堆內存: 4900 KB | cache大小: 10 | cache2大小: 0* cache 大小: 10* cache2 大小: 0* @param args*/public static void main(String[] args) {printMemoryStats("程序啟動");testStringLiteral();  // 內存持續增長forceGCAndWait();testNewString();      // 內存穩定forceGCAndWait();// 最終報告System.out.println("\n===== 最終內存報告 =====");printMemoryStats("最終狀態");System.out.println("cache 大小: " + cache.size());System.out.println("cache2 大小: " + cache2.size());}
四、虛引用
說明

虛引用是4個引用中弱最的引用類型,它通過PhantomReference類創建的,并且必須需要配合ReferenceQueue隊列使用

,通過虛引用的get()獲取對象總是返回null,在對象被GC回收時ReferenceQueue隊列可以收到回收通知,如下

    public static void main(String[] args) throws InterruptedException {       // 1. 創建引用隊列(用于接收被回收對象的虛引用)ReferenceQueue<MyResource> queue = new ReferenceQueue<>();// 2. 創建資源對象MyResource myResource = new MyResource("重要資源");// 3. 創建虛引用,關聯資源對象和引用隊列PhantomReference<MyResource> phantomRef =new PhantomReference<>(myResource, queue);System.out.println("初始狀態:");System.out.println("  資源對象: " + myResource);System.out.println("  虛引用是否指向對象: " + (phantomRef.get() != null)); // 虛引用總是返回nullSystem.out.println("  引用隊列是否有數據: " + (queue.poll() != null));System.out.println();// 4. 斷開強引用,使資源對象可被回收myResource = null;// 5. 請求垃圾回收(注意:這只是建議,不保證立即執行),多次請求GC(增加成功率)for (int i = 0; i < 3; i++) {System.gc();System.runFinalization();}// 6. 給GC一點時間執行Thread.sleep(500);System.out.println("GC后狀態:");System.out.println("  虛引用是否指向對象: " + (phantomRef.get() != null));// 7. 檢查引用隊列(虛引用會在對象回收后被加入隊列),阻塞方式等待虛引用入隊(最多等待2秒)PhantomReference<?> refFromQueue = (PhantomReference<?>) queue.remove(2000);if (refFromQueue != null) {System.out.println("? 檢測到資源已被回收,虛引用進入隊列");System.out.println("  隊列中的引用: " + refFromQueue);System.out.println("  是否與原始虛引用相同: " + (refFromQueue == phantomRef));// 這里可以執行資源清理操作// resource 普通堆內存對象,JVM 自動回收這個對象占用的堆內存,虛引用入隊只是通知你"對象已被回收,不需要手動釋放堆內存//            System.out.println("執行清理操作:釋放資源關聯的內存...");} else {System.out.println("? 超過等待時間仍未檢測到資源回收");System.out.println("可能原因:");System.out.println("1. GC尚未執行完成");System.out.println("2. 對象仍有強引用");System.out.println("3. JVM忽略了System.gc()");}}static class MyResource {private final String name;public MyResource(String name) {this.name = name;System.out.println("創建資源: " + name);}@Overrideprotected void finalize() throws Throwable {System.out.println("🔥 垃圾回收器正在回收資源: " + name);super.finalize();}}
使用場景舉例

在精確控制資源釋放的場景經常使用,在日常業務開發中較少直接使用,但在基礎框架、中間件、高性能庫中(如Netty、JDK NIO等)有不可替代的作用

在虛引用機制中,虛引用本身不負責釋放內存,它的核心作用是提供對象被回收的通知時機,讓開發者有機會執行自定義的清理邏輯。是否需要手動釋放內存取決于資源的類型,如下:

內存類型釋放責任方虛引用中的作用
堆內存JVM自動回收無需處理
堆外內存開發者手動釋放在清理回調中釋放
其他資源開發者手動釋放在清理回調中關閉/釋放

例子:

/*** 虛引用例子*/
public class PhantomReferenceExample2 {// 資源清理接口@FunctionalInterfacepublic interface ResourceCleaner {void clean();}// 自定義虛引用(攜帶清理邏輯)private static class MemoryReference extends PhantomReference<ByteBuffer> {private final ResourceCleaner cleaner;private final Object jdkCleaner; // 改為 Object 類型private final int size;public MemoryReference(ByteBuffer referent,ReferenceQueue<? super ByteBuffer> queue,ResourceCleaner cleaner,Object jdkCleaner, // 改為 Objectint size) {super(referent, queue);this.cleaner = cleaner;this.jdkCleaner = jdkCleaner;this.size = size;}public void clean() {cleaner.clean(); // 執行自定義清理邏輯cleanMemory();   // 實際釋放堆外內存System.out.printf("? 釋放堆外內存: 大小=%,d bytes\n", size);}private void cleanMemory() {try {// 通過反射調用 clean() 方法Method cleanMethod = jdkCleaner.getClass().getMethod("clean");cleanMethod.invoke(jdkCleaner);} catch (Exception e) {throw new RuntimeException("無法調用clean方法", e);}}}// 內存管理器public static class MemoryManager implements AutoCloseable {private final ReferenceQueue<ByteBuffer> queue = new ReferenceQueue<>();private final ConcurrentHashMap<MemoryReference, Boolean> refs = new ConcurrentHashMap<>();private final ExecutorService cleanerThread = Executors.newSingleThreadExecutor();private volatile boolean running = true;public MemoryManager() {// 啟動后臺清理線程cleanerThread.submit(() -> {while (running || !refs.isEmpty()) {try {MemoryReference ref = (MemoryReference) queue.remove(500);if (ref != null) {ref.clean();refs.remove(ref);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}});}/*** 分配堆外內存并返回ByteBuffer*/public ByteBuffer allocateDirect(int size) {// 分配堆外內存ByteBuffer buffer = ByteBuffer.allocateDirect(size);// 獲取JDK內置的CleanerObject jdkCleaner = getCleaner(buffer);// 創建自定義清理邏輯ResourceCleaner customCleaner = () -> {System.out.println("執行自定義清理操作...");};// 創建虛引用并注冊MemoryReference ref = new MemoryReference(buffer, queue, customCleaner, jdkCleaner, size);refs.put(ref, Boolean.TRUE);return buffer;}@Overridepublic void close() throws Exception {running = false;cleanerThread.shutdown();if (!cleanerThread.awaitTermination(5, TimeUnit.SECONDS)) {cleanerThread.shutdownNow();}System.out.println("內存管理器已關閉");}/*** 獲取ByteBuffer關聯的Cleaner*/private Object getCleaner(ByteBuffer buffer) {try {// 獲取ByteBuffer的cleaner()方法Method cleanerMethod = buffer.getClass().getMethod("cleaner");cleanerMethod.setAccessible(true);// 調用cleaner()方法獲取Cleaner實例return cleanerMethod.invoke(buffer);} catch (Exception e) {throw new RuntimeException("無法獲取Cleaner", e);}}}/**** VM options 中,需要添加兩個參數:* --add-opens java.base/java.nio=ALL-UNNAMED* --add-opens java.base/jdk.internal.ref=ALL-UNNAMED** @param args* @throws Exception*/public static void main(String[] args) throws Exception {try (MemoryManager manager = new MemoryManager()) {// 分配堆外內存,但是實例元信息還是在堆中,內存塊在堆外ByteBuffer buffer = manager.allocateDirect(1024 * 1024); // 1MB// 使用緩沖區buffer.putInt(0, 42);System.out.println("緩沖區值: " + buffer.getInt(0));// 釋放緩沖區,取消強引用,使之實例元信息可以被GC回收// 包含指向堆外內存的指針和其他元數據(注意此處不是元空間的元數據,兩者是不同概念)// 只有這個Java對象被回收后,虛引用才會被加入隊列,// 虛引用是橋梁:當堆中的 ByteBuffer 對象被回收時,虛引用會觸發回調來釋放堆外內存buffer = null;// 模擬GC,實際生產環境中,不會使用System.gc(),而是使用JVM的垃圾回收器System.gc();Thread.sleep(1000);}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/86247.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/86247.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/86247.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue 瀏覽器樣式警告:“unknown property name“

瀏覽器樣式警告&#xff1a;"unknown property name"&#xff0c;但在部分電腦上的瀏覽器又是沒有問題的。 這個問題因為沒有安裝sass或者less&#xff0c;卻直接使用了他的語法&#xff0c;比如嵌套樣式&#xff1a; body {/* 按鈕 */.el-button {background: lin…

postgresql DDL腳本

在PostgreSQL中&#xff0c;數據定義語言&#xff08;DDL&#xff09;腳本用于定義、修改或刪除數據庫的結構。DDL 操作包括創建表、修改表結構、刪除表等。以下是幾種常見的DDL操作示例以及如何在腳本中實現它們。 1. 創建表 CREATE TABLE employees (employee_id SERIAL PR…

C#語言入門-task3 :C# 語言的面向對象技術

C# 面向對象編程技術概述 C# 是一種現代化的面向對象編程語言&#xff0c;提供了豐富的特性來支持面向對象的編程范式。以下是 C# 中面向對象編程的核心概念和技術&#xff1a; 核心概念 1. 類與對象 類是對象的藍圖&#xff0c;定義了對象的屬性和行為。對象是類的實例。 …

感知框2D反投是咋回事?

一、感知框&#xff1a;“2D 框反投” 是咋回事&#xff1f;&#xff08;以自動駕駛識別車輛為例&#xff09; 1. 核心邏輯&#xff1a;從圖像特征 “反推” 目標框 簡單說&#xff0c;先用算法在 2D 圖像里識別特征&#xff08;比如車輛的輪廓、顏色、紋理&#xff09;&#…

五分鐘了解@ExcelIgnoreUnannotated注解

ExcelIgnoreUnannotated 是 EasyExcel 框架中的一個注解&#xff0c;用于控制 Excel 導入/導出時對實體類字段的處理方式。它的作用如下&#xff1a; 核心功能 忽略未標注 ExcelProperty 的字段 當添加 ExcelIgnoreUnannotated 到類上時&#xff0c;EasyExcel 會 跳過所有未顯…

Windows電腦數據恢復終極指南:從原理到實戰

Windows電腦數據恢復終極指南&#xff1a;從原理到實戰 數據丟失是每個電腦用戶都可能遭遇的噩夢。本文將為您全面解析Windows平臺下的數據恢復技術&#xff0c;從基礎原理到高級技巧&#xff0c;幫助您在文件誤刪、格式化、系統崩潰等情況下找回寶貴數據。 一、數據恢復基礎…

【網絡入侵檢測】基于Suricata源碼分析應用協議識別實現

【作者主頁】只道當時是尋常 【專欄介紹】Suricata入侵檢測。專注網絡、主機安全&#xff0c;歡迎關注與評論。 1. 概要 &#x1f44b; 本文聚焦Suricata網絡安全引擎的協議解析器實現&#xff0c;詳細剖析HTTP、SSL/TLS、FTP、SSH、SMTP等協議的解析流程。 2. 源碼分析 2.1 H…

Nginx SSL/TLS協議棧中配置深度解析與實踐指南-優雅草卓伊凡

Nginx SSL/TLS協議棧中配置深度解析與實踐指南-優雅草卓伊凡 引言&#xff1a;SSL/TLS的重要性與Nginx配置挑戰 在當今互聯網環境中&#xff0c;SSL/TLS加密已成為網站安全的基本要求。根據Google透明度報告顯示&#xff0c;截至2023年&#xff0c;全球Chrome瀏覽器加載的網頁…

C++字符串的行輸入

1、字符串的輸入 下面用一個真實的示例來進行演示&#xff1a; #include<iostream> #include<string>int main() {using namespace std;const int ArSize 20;char name[ArSize];char dessert[ArSize];cout << "Enter your name:\n";cin >>…

征服分布式系統:阿里云 Linux 多機互聯與資源共享實戰指南

征服分布式系統&#xff1a;阿里云 Linux 多機互聯與資源共享實戰指南 文章目錄 征服分布式系統&#xff1a;阿里云 Linux 多機互聯與資源共享實戰指南一、分布式系統架構概述二、阿里云網絡基礎架構解析三、多機互聯基礎配置1. 環境準備2. 網絡連通性測試3. SSH 密鑰認證配置 …

AI三步診斷心理:比ChatGPT更懂人心

用人工智能(大語言模型)輔助心理治療的研究 解決心理治療中專業人員不足的問題,提出了一種叫“思維診斷”(DoT)的方法,讓AI通過三個步驟來識別患者的“認知扭曲”(也就是負面、不合理的思維模式)。 背景:心理治療的困境 全世界約八分之一的人有心理問題,但心理咨詢師…

C++【生存游戲】開發:荒島往事 第一期

字幕君已上線...... 副字幕君已上線...... 計數君已上線...... 彩色字幕君 ( 花了重金請來的 ) 已上線...... Doge智能系統已上線...... Doge:嗨嗨我又來了&#xff01; 觀眾們......已上線&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; OK LETS GO&am…

k8s強制刪除podpvpvc和nsnamespace

k8s如何強制刪除pod&pv&pvc和ns&namespace方法 namespace、pod、pv、pvc全部處于“Terminating”狀態時&#xff0c;此時的該名稱空間下的所有控制器都已經被刪除了&#xff0c;之所以出現pod、pvc、pv、ns無法刪除&#xff0c;那是因為kubelet 阻塞&#xff0c;有…

Python基礎--4--Python常用代碼示例

1、文件讀寫操作&#xff08;常用于日志處理、數據導入導出&#xff09; 1、代碼示例 &#xff08;1&#xff09;讀取文本文件內容 with open("data.txt", "r", encoding"utf-8") as f:content f.read()print(content)&#xff08;2&#xf…

單項循環鏈表及帶頭指針的鏈表

單項循環鏈表及其帶頭指針的鏈表 對于鏈表我們要仔細深入的學習它&#xff0c;為何呢&#xff0c;因為他是我們在后面學習非線性數據結構的基礎&#xff0c;像后面的樹&#xff0c;圖等結構都是由鏈表演變出來的&#xff0c;所以我們這篇博客繼續探究鏈表 帶頭指針的鏈表 我…

八股文——JAVA基礎:解釋下什么是面向對象?面向對象和面向過程的區別

面向對象和面向過程是編程的不同思想&#xff1a; 面向過程如c語言的編程形式&#xff0c;在編程時定義的是一個方法&#xff0c;然后后續執行只需要關注這個方法的作用&#xff0c;而不會將方法進行抽象&#xff0c;也就是只關注程序執行的過程細節。 面向對象如java&#x…

SuperMap iServer 關閉數據目錄(datacatalog)、地圖打印(webprinting)等服務

背景 漏洞掃描發現有部分低危 web 漏洞&#xff0c;項目又暫未使用數據目錄服務&#xff0c;所以最簡單的方案是直接關閉服務。 查閱文檔發現處理自動化服務可以修改webapps\iserver\WEB-INF\iserver-geoprocessing.xml 的 enable 屬性為 false 關閉&#xff0c;機器學習服務…

PyTorch 張量(Tensors)全面指南:從基礎到實戰

文章目錄 什么是張量&#xff1f;張量初始化方法1. 直接從數據創建2. 從 NumPy 數組轉換3. 基于現有張量創建4. 使用隨機值或常量 張量屬性張量操作設備轉移索引和切片連接張量算術運算單元素張量轉換 原地操作&#xff08;In-place Operations&#xff09;PyTorch 與 NumPy 互…

Maven是什么?

Maven是一個流行的Java項目管理和構建工具&#xff0c;主要用于自動化項目構建、依賴管理和項目文檔生成等工作。以下是對它的簡單介紹&#xff1a; 核心功能 依賴管理&#xff1a;自動管理項目所需的第三方庫&#xff08;如JAR包&#xff09;&#xff0c;通過在配置文件中聲…

etcd教程-快速入門使用(截圖實操)集群搭建 + 原理解釋

大家好&#xff0c;我是此林。 etcd 是一個高可用的鍵值對存儲系統&#xff0c;常用于分布式系統中保存配置、服務發現和協調信息。它是 CNCF 旗下的項目之一&#xff0c;也是 Kubernetes 的核心組件之一&#xff0c;用來存儲集群狀態。 可以說&#xff0c;云原生場景下經常使…