首先一個大的警告:「不安全必須死」http://blog.takipi.com/still-unsafe-the-major-bug-in-java-6-that-turned-into-a-java-9-feature/
一些先決條件
static class DataHolder {
int i1;
int i2;
int i3;
DataHolder d1;
DataHolder d2;
public DataHolder(int i1, int i2, int i3, DataHolder dh) {
this.i1 = i1;
this.i2 = i2;
this.i3 = i3;
this.d1 = dh;
this.d2 = this;
}
}
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
DataHolder dh1 = new DataHolder(11, 13, 17, null);
DataHolder dh2 = new DataHolder(23, 29, 31, dh1);
的基礎
爲了得到一個場(I1)的偏移量,你可以使用以下代碼:
Field fi1 = DataHolder.class.getDeclaredField("i1");
long oi1 = unsafe.objectFieldOffset(fi1);
和訪問實例字段的值DH1你可以寫
System.out.println(unsafe.getInt(dh1, oi1)); // will print 11
可以使用類似的代碼來訪問一個對象引用(D1):
Field fd1 = DataHolder.class.getDeclaredField("d1");
long od1 = unsafe.objectFieldOffset(fd1);
,你可以用它來獲取基準從DH2 DH1:
System.out.println(dh1 == unsafe.getObject(dh2, od1)); // will print true
現場訂貨,並對準
要獲得附加賽所有已聲明對象的字段:
for (Field f: DataHolder.class.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) {
System.out.println(f.getName()+" "+unsafe.objectFieldOffset(f));
}
}
在我的測試中,似乎JVM按照它認爲合適的順序重新排列字段(即,添加字段可以產生在一次運行完全不同的偏移量)
的本機內存中的對象地址
明白,下面的代碼是要崩潰的JVM遲早是很重要的,因爲垃圾收集器會隨機移動您的物體,而無需控制何時以及爲何發生。
此外,瞭解以下代碼依賴於JVM類型(32位與64位)以及JVM的一些啓動參數(即在64位JVM上使用壓縮的oops)很重要。
在32位虛擬機上,對象的引用與int具有相同的大小。那麼如果你撥打int addr = unsafe.getInt(dh2, od1));
而不是unsafe.getObject(dh2, od1))
,你會得到什麼?它可能是對象的本機地址嗎?
讓我們試試:
System.out.println(unsafe.getInt(null, unsafe.getInt(dh2, od1)+oi1));
會打印出11
預期。
在64位虛擬機,而不壓縮糟糕(-XX:-UseCompressedOops),您將需要編寫
System.out.println(unsafe.getInt(null, unsafe.getLong(dh2, od1)+oi1));
在64位虛擬機與壓縮糟糕(-XX:+ UseCompressedOops),事情有點複雜。該變體具有通過將它們與8L相乘變成64個地址的32位對象的引用:
System.out.println(unsafe.getInt(null, 8L*(0xffffffffL&(dh2, od1)+oi1));
什麼是與這些訪問
的問題是垃圾收集器與該代碼一起的問題。垃圾收集器可以隨意移動物體。由於JVM知道它是對象引用(局部變量dh1和dh2,這些對象的字段d1和d2),因此它可以相應地調整這些引用,所以您的代碼將永遠不會注意到。
通過將對象引用提取到int/long變量中,可以將這些對象引用轉換爲碰巧與對象引用具有相同位模式的原始值,但垃圾收集器不知道這些是對象引用(它們可以也是由隨機生成器生成的),因此在移動物體時不會調整這些值。因此,一旦垃圾收集週期被觸發,您提取的地址就不再有效,並且嘗試訪問這些地址處的內存可能會立即導致JVM崩潰(正常情況),或者您可能會在不注意現場的情況下垃圾內存(壞的案件)。
爲什麼你想這樣做而不是設計語言的設計方式? –
主要是出於好奇(知道可以做什麼和不可以做什麼的限制),還因爲我碰巧正在編寫一款需要與大型聲音文件一起工作並且能夠同時實時處理圖像的軟件使用什麼可能是我的過於複雜的算法,現在,我的概念驗證運行速度低於60fps,我認爲這可能會提高性能。我沒有學術知識,所以我可以做的最好的改進算法是非常基礎的(避免太多的實例化,儘可能內聯的東西,如果可能的話使用位移而不是算術運算等) – FinnTheHuman
你讀過它的javadocs還是看過使用它的代碼,例如在github上還是在jdk本身? – the8472