sun.misc.Cleaner實現堆外內存回收

簡介

項目中採用了java+c的混合開發,通過jni進行了底層結構體的內存分配,將指針返回給java層保存,隨後則可以通過傳遞指針值來操作底層代碼。在java中,仍然需要手動釋放jni分配出來的內存的。

如何讓GC來自動管理jni內存

sun.misc.Cleaner可以做到!
話不多說,直接看例子。

示例

import sun.misc.Cleaner;

public class Main {
    public static class FreeMemoryTask implements Runnable {
        private long address;

        public FreeMemoryTask(long address) {
            this.address = address;
        }

        @Override
        public void run() {
            // 有地址可以進行回收
            System.out.println("垃圾回收觸發" + address);
        }
    }

    public static class MyObject {
        public static int a = 0;
        private long id = 0;
        private byte[] array;

        public MyObject() {
            id = ++a;
            array = new byte[1024 * 1024 * 10];
            System.out.println("創建了對象" + id);
        }

        public static void main(String[] args) throws Exception {

            while (true) {
//                System.gc();
                MyObject object = new MyObject();
                // Cleaner.create 檢測對象回收,回收時觸發FreeMemoryTask
                Cleaner.create(object, new FreeMemoryTask(object.id));
                Thread.sleep(1000);
            }
        }
    }
}

運行效果

創建了對象1
創建了對象2
創建了對象3
創建了對象4
創建了對象5
創建了對象6
創建了對象7
創建了對象8
創建了對象9
創建了對象10
垃圾回收觸發1
垃圾回收觸發2
垃圾回收觸發7
垃圾回收觸發6
垃圾回收觸發5
垃圾回收觸發3
垃圾回收觸發4
垃圾回收觸發10
垃圾回收觸發9
垃圾回收觸發8
創建了對象11
創建了對象12
創建了對象13
創建了對象14
創建了對象15
創建了對象16
創建了對象17

在這裏插入圖片描述

說明

在發生GC時,就會觸發FreeMemoryTask。只需要將指針地址交給它,就可以進行垃圾回收。

優點

  1. 比finalizer更輕量更好用。
  2. 遇到該對象共享給多個線程,大部分時間只讀,少部分時間更新時,釋放需要用讀寫鎖確保安全。但採用Cleaner則無需鎖,更加高效。

netty中的DirectByteBuffer

通過一系列的源碼跟蹤,最終會發現,其回收相關的代碼如下:

DirectByteBuffer(int cap) {                   // package-private	
    ……
    Cleaner.create(this, new Deallocator(base, size, cap));
    ……
}

private static class Deallocator implements Runnable
{

    private static Unsafe unsafe = Unsafe.getUnsafe();

    private long address;
    private long size;
    private int capacity;

    private Deallocator(long address, long size, int capacity) {
        assert (address != 0);
        this.address = address;
        this.size = size;
        this.capacity = capacity;
    }

    public void run() {
        if (address == 0) {
            // Paranoia
            return;
        }
        unsafe.freeMemory(address);
        address = 0;
        Bits.unreserveMemory(size, capacity);
    }

}

從這段源碼來看,也就是說,即使忘記手動釋放資源,在對象能被GC的情況下,應該也是能夠通過正常的GC回收掉堆外內存的,這點暫不確認。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章