① 如何使用Unsafe操作內存中的java類和對象
在Java中最直接的內存操作方法是什麼?
Java最初被設計為一種安全的受控環境。盡管如此,Java HotSpot還是包含了一個「後門」,提供了一些可以直接操控內存和線程的低層次操作。這個後門類——sun.misc.Unsafe——被JDK廣泛用於自己的包中,如java.nio和java.util.concurrent。但是絲毫不建議在生產環境中使用這個後門。因為這個API十分不安全、不輕便、而且不穩定。這個不安全的類提供了一個觀察HotSpot JVM內部結構並且可以對其進行修改。有時它可以被用來在不適用C++調試的情況下學習虛擬機內部結構,有時也可以被拿來做性能監控和開發工具。
為何變得不安全
sun.misc.Unsafe這個類是如此地不安全,以至於JDK開發者增加了很多特殊限制來訪問它。它的構造器是私有的,工廠方法getUnsafe()的調用器只能被Bootloader載入。如你在下面代碼片段的第8行所見,這個傢伙甚至沒有被任何類載入器載入,所以它的類載入器是null。它會拋出SecurityException 異常來阻止侵入者。
② JAVA POI 使用addMergedRegionUnsafe合並單元格操作導致導出時間大大增加,怎麼解決
可以邏輯合並,直接把excel文件後綴名改zip,進去後進入目錄xl/sheet目錄下,打開sheet1.xml如下
這是橫向合並,spans="1:2"中的1代表第1行2代表合並ab兩列,具體可以根據代碼邏輯修改,這種直接修改源文件的方式是效率最快的,邏輯也是最復雜的,具體看自己取捨吧
③ Java為什麼會引入及如何使用Unsafe
sun.misc.Unsafe至少從2004年Java1.4開始就存在於Java中了。在Java9中,為了提高JVM的可維護性,Unsafe和許多其他的東西一起都被作為內部使用類隱藏起來了。但是究竟是什麼取代Unsafe不得而知,個人推測會有不止一樣來取代它,那麼問題來了,到底為什麼要使用Unsafe?
做一些Java語言不允許但是又十分有用的事情
很多低級語言中可用的技巧在Java中都是不被允許的。對大多數開發者而言這是件好事,既可以拯救你,也可以拯救你的同事們。同樣也使得導入開源代碼更容易了,因為你能掌握它們可以造成的最大的災難上限。或者至少明確你可以不小心失誤的界限。如果你嘗試地足夠努力,你也能造成損害。
那你可能會奇怪,為什麼還要去嘗試呢?當建立庫時,Unsafe中很多(但不是所有)方法都很有用,且有些情況下,除了使用JNI,沒有其他方法做同樣的事情,即使它可能會更加危險同時也會失去Java的「一次編譯,永久運行」的跨平台特性。
對象的反序列化
當使用框架反序列化或者構建對象時,會假設從已存在的對象中重建,你期望使用反射來調用類的設置函數,或者更准確一點是能直接設置內部欄位甚至是final欄位的函數。問題是你想創建一個對象的實例,但你實際上又不需要構造函數,因為它可能會使問題更加困難而且會有副作用。
public class A implements Serializable {
private final int num;
public A(int num) {
System.out.println("Hello Mum");
this.num = num;
}
public int getNum() {
return num;
}
}
在這個類中,應該能夠重建和設置final欄位,但如果你不得不調用構造函數時,它就可能做一些和反序列化無關的事情。有了這些原因,很多庫使用Unsafe創建實例而不是調用構造函數。
Unsafe unsafe = getUnsafe();
Class aClass = A.class;
A a = (A) unsafe.allocateInstance(aClass);
調用allocateInstance函數避免了在我們不需要構造函數的時候卻調用它。
線程安全的直接獲取內存
Unsafe的另外一個用途是線程安全的獲取非堆內存。ByteBuffer函數也能使你安全的獲取非堆內存或是DirectMemory,但它不會提供任何線程安全的操作。你在進程間共享數據時使用Unsafe尤其有用。
import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class PingPongMapMain {
public static void main(String... args) throws IOException {
boolean odd;
switch (args.length < 1 ? "usage" : args[0].toLowerCase()) {
case "odd":
odd = true;
break;
case "even":
odd = false;
break;
default:
System.err.println("Usage: java PingPongMain [odd|even]");
return;
}
int runs = 10000000;
long start = 0;
System.out.println("Waiting for the other odd/even");
File counters = new File(System.getProperty("java.io.tmpdir"), "counters.deleteme");
counters.deleteOnExit();
try (FileChannel fc = new RandomAccessFile(counters, "rw").getChannel()) {
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
long address = ((DirectBuffer) mbb).address();
for (int i = -1; i < runs; i++) {
for (; ; ) {
long value = UNSAFE.getLongVolatile(null, address);
boolean isOdd = (value & 1) != 0;
if (isOdd != odd)
// wait for the other side.
continue;
// make the change atomic, just in case there is more than one odd/even process
if (UNSAFE.compareAndSwapLong(null, address, value, value + 1))
break;
}
if (i == 0) {
System.out.println("Started");
start = System.nanoTime();
}
}
}
System.out.printf("... Finished, average ping/pong took %,d ns%n",
(System.nanoTime() - start) / runs);
}
static final Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
當你分別在兩個程序,一個輸入odd一個輸入even,中運行時,可以看到兩個進程都是通過持久化共享內存交換數據的。
在每個程序中,將相同的磁碟緩存映射到進程中。內存中實際上只有一份文件的副本存在。這意味著內存可以共享,前提是你使用線程安全的操作,比如volatile變數和CAS操作。(譯註:CAS Compare and Swap 無鎖演算法)
在兩個進程之間有83ns的往返時間。當考慮到System V IPC(進程間通信)大約需要2500ns,而且用IPC volatile替代persisted內存,算是相當快的了。
Unsafe適合在工作中使用嗎?
個人不建議直接使用Unsafe。它遠比原生的Java開發所需要的測試多。基於這個原因建議還是使用經過測試的庫。如果你只是想自己用Unsafe,建議你最好在一個獨立的類庫中進行全面的測試。這限制了Unsafe在你的應用程序中的使用方式,但會給你一個更安全的Unsafe。
總結
Unsafe在Java中是很有趣的一個存在,你可以一個人在家裡隨便玩玩。它也有一些工作的應用程序特別是在寫底層庫的時候,但總的來說,使用經過測試的Unsafe庫比直接用要好。
④ 求教java中的unsafe.allocateMemory 會導致內存申請失敗嗎
一:Java內存區域與內存溢出異常
在運行Java程序時,Java虛擬機會把管理的內存劃分為若干個不同的數據區域。
Java虛擬機運行時數據區
數據區域圖中,除了方法區和堆區是線程共享區外,其他三個是線程隔離的數據區(private)
程序計數器(Program Counter Register):屬於線程私有的,佔用的內存空間較少,可以看成是當前線程所執行位元組碼的行號指示器,位元組碼解釋器工作時就是通過改變這個計數器的值來選擇下一條,需要執行的位元組碼指令,分支,循環,跳轉,異常處理,線程恢復等基礎功能需要依賴這個計數器來完成,這個區域是jvm規范中沒有規定任何OutOfMemoryError情況區域。
虛擬機棧:和程序計數器一樣,都屬於線程私有,生命周期與線程相同,描述的是java方法執行的內存模型,每個方法執行都會創建一個棧幀,用於存儲局部變數表,操作棧,動態鏈接,方法出口等信息,每一個方法被調用直至執行完成的過程,就對應一個棧幀在jvm stack 從入棧到出棧的過程.局部變數表存放了編譯期可知的各種數據基本類型(Boolean,byte,char,short,int,float,long,double),以及對象的引用。這個區域中定義了2種異常情況,如果線程請求的棧深度大於jvm所允許的深度,將拋出StackOverflowError異常,如果jvm可以動態擴張,當擴張無法申請到足夠的內存空間是會拋出OutOfMemoryError異常。(這些數據區域異常將在下面的例子都講到)。
本地方法棧:與虛擬機棧比較相似。其區別:虛擬機棧為虛擬機執行Java方法服務,而本地方法棧則為虛擬機使用Native方法服務。
堆(Heap):jvm中內存佔用最大的一塊,是所有線程共享的一塊內存區域.在jvm啟動時創建,存放的是所有對象實例(或數組),所有的對象實例都在這里進行動態分配,當類空間無法再擴張會拋出OutOfMemoryError異常。Java堆是垃圾收集器管理的主要區域,而收集器採用分代收集演算法。
方法區(Method Area):與堆類似,也是各個線程共享的內存區域,主要用來存儲載入的類信息,常量,靜態變數,即時編譯器編譯後的代碼等數據,當方法區無法滿足內存分配時,也拋出OutOfMemoryError異常。運行時常量池是方法區的一部分,用於存放編譯期生成的各種字面量和符號引用相對於Class文件常量池的重要特徵是具備動態性(常量並非強制編譯期產生,運行期間也可以新增,例如String類的intern()方法)。
直接內存(DirectMemort):並不屬於數據區,也不屬於java定義的內存區域。由於NIO(New Input/Output)類,引入了一種基於通道與緩沖區(Buffer)的I/O方式。
對象訪問
Object object = new Object();
Object object 這部分存儲在java棧的本地變數表中,作為一個引用(reference)類型存在。
new Object() 這部分存儲在java堆中,形成了一塊存儲了Object類型所有的實例數據值的結構化內存,動態變化,長度不固定。
方法區:在java堆中,必須要找到此對象類型數據,比如,對象類型,基類,實現的介面,方法等都存放在方法區。
對象訪問方式有兩種:句柄和直接指針。
句柄:reference中存儲是對象的句柄地址,而句柄包含了對象實例數據和類型數據各自具體地址信息。好處:在對象移動時只需改變句柄中的實例數據指針,reference本身不需要修改。
直接指針:reference中直接存儲的就是對象地址。好處:速度快,它節省了一次指針定位的時間開銷。
實戰:OutOfMemoryError異常
1. Java堆溢出
調整虛擬機最小值(-Xms)和最大值(-Xmx),並通過參數-XX:+HeapDumpOnOutOfMemoryError生成快照。要解決這個區域的異常,通過內存映像分析工具對快照分析,確認內存中的對象是否是必要的,分清楚出現了內存泄露還是內存溢出。若是內存泄露,通過工具查看泄露對象到GCRoots引用鏈,找到泄露對象是通過怎樣的路徑與GCRoots相關聯並導致垃圾收集器無法自動回收。若不存在泄露,則檢查虛擬機堆參數與機器物理內存對比看是否還能調大或從代碼上檢查某些對象生命周期是否過長,嘗試減少程序運行期的內存消耗。
2. 虛擬機棧和本地方法棧溢出
調節棧容量大小(-Xss)。如果線程請求的棧深度大於虛擬機所允許的最大深度,將會拋出StackOverflowError異常。使用-Xss參數減小棧內存容量或者增加此方法幀中本地變數表的程度都使棧深度縮小。
3. 運行時常量池溢出
調節參數-XX:PermSize和-XX:MaxPermSize限制方法區的大小,然後使用String.intern()這個Native方法向常量池中添加內容。運行時常量池溢出,在OutOfMemoryError後面跟隨提示信息是「PermGen space」,說明運行時常量池屬於方法區(HotSpot虛擬機的永久代)的一部分。
4. 方法區溢出
同樣使用參數-XX:PermSize和-XX:MaxPermSize限制方法區的大小,然後不斷產生大量的class來載入到內存,從而出現OutOfMemoryError。所以在經常動態生成大量Class的應用中,需要特別注意類的回收狀況。
5. 本機直接內存溢出
通過參數-XX:MaxDirectMemorySize指定DirectMemory容量,若不指定則與Java堆最大值一樣。可以直接通過反射獲取Unsafe實例並進行內存分配,使用unsafe.allocateMemory()申請分配內存。不足時會出現OutOfMemoryError。
二.垃圾收集器與內存分配策略
概述
Java內存運行時區域的各個部分,其中程序計數器、VM棧、本地方法棧三個區域隨線程而生,隨線程而滅;棧中的幀隨著方法進入、退出而有條不紊的進行著出棧入棧操作。而Java堆和方法區(包括運行時常量池)則不一樣,我們必須等到程序實際運行期間才能知道會創建哪些對象,這部分內存的分配和回收都是動態的。
判斷對象已死
1)引用計數演算法(對象中添加一個引用計數器,當有一個地方引用它,計數器加1,當引用失效,計數器減1,任何時刻計數器為0的對象就是不可能再被使用的),但引用計數演算法無法解決對象循環引用的問題。
根搜索演算法(通過一系列的稱為「GCRoots」的點作為起始進行向下搜索,當一個對象到GCRoots沒有任何引用鏈(ReferenceChain)相連,則證明此對象是不可用的),主流程序語言Java,c#都使用此演算法。在Java語言中,GC Roots包括:
1.在VM棧(幀中的本地變數)中的引用。2.方法區中的靜態引用和常量引用的對象。3.JNI(即一般說的Native方法)中的引用。
2)生存還是死亡?
判定一個對象死亡,至少經歷兩次標記過程:如果對象在進行根搜索後,發現沒有與GC Roots相連接的引用鏈,那它將會被第一次標記,並在稍後執行他的finalize()方法(如果它有的話)。這里所謂的「執行」是指虛擬機會觸發這個方法,但並不承諾會等待它運行結束。這點是必須的,否則一個對象在finalize()方法執行緩慢,甚至有死循環什麼的將會很容易導致整個系統崩潰。 finalize()方法是對象最後一次逃脫死亡命運的機會,稍後GC將進行第二次規模稍小的標記,如果在finalize()中對象成功拯救自己(只要重新建立到GC Roots的連接即可,譬如把自己賦值到某個引用上),那在第二次標記時它將被移除出「即將回收」的集合,如果對象這時候還沒有逃脫,那基本上它就真的離死不遠了。需要關閉外部資源之類的事情,基本上它能做的使用try-finally可以做的更好。
3)回收方法區
方法區即後文提到的永久代,很多人認為永久代是沒有GC的,這區GC的「性價比」一般比較低:在堆中,尤其是在新生代,進行一次GC可以一般可以回收70%~95%的空間,而永久代的GC效率遠小於此。但是目前方法區主要回收兩部分內容:廢棄常量與無用類。需要滿足下面3個條件:1.該類所有的實例都已經被GC,也就是JVM中不存在該Class的任何實例。2.載入該類的ClassLoader已經被GC。3.該類對應的java.lang.Class對象沒有在任何地方被引用,如不能在任何地方通過反射訪問該類的方法。
垃圾收集演算法
1.標記-清除演算法(Mark-Sweep)
演算法分成「標記」和「清除」兩個階段,首先標記出所有需要回收的對象,然後回收所有需要回收的對象。主要缺點有兩個,一是效率問題,標記和清理兩個過程效率都不高,二是空間問題,標記清理之後會產生大量不連續的內存碎片,空間碎片太多可能會導致後續使用中無法找到足夠的連續內存而提前觸發另一次的垃圾搜集動作。
2.復制演算法(Copying)
將內存分為一塊較大的eden空間和2塊較少的survivor空間,每次使用eden和其中一塊survivor,當回收時將eden和 survivor還存活的對象一次過拷貝到另外一塊survivor空間上,然後清理掉eden和用過的survivor。復制收集演算法在對象存活率高的時候,效率有所下降。
3.標記-整理(Mark-Compact)演算法
標記過程仍然一樣,但後續步驟不是進行直接清理,而是令所有存活的對象一端移動,然後直接清理掉這端邊界以外的內存。
4.分代收集(Generational Collection)演算法
此演算法只是根據對象不同的存活周期將內存劃分為幾塊。一般是把Java堆分作新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。
垃圾收集器
沒有最好的收集器,也沒有萬能的收集器,只有最合適的收集器。
1.Serial收集器單線程收集器,收集時會暫停所有工作線程(我們將這件事情稱之為Stop The World,下稱STW),使用復制收集演算法,虛擬機運行在Client模式時的默認新生代收集器。2.ParNew收集器ParNew收集器就是Serial的多線程版本,除了使用多條收集線程外,其餘行為包括演算法、STW、對象分配規則、回收策略等都與Serial收集器一摸一樣。對應的這種收集器是虛擬機運行在Server模式的默認新生代收集器,在單CPU的環境中,ParNew收集器並不會比Serial收集器有更好的效果。3.Parallel Scavenge收集器Parallel Scavenge收集器(下稱PS收集器)也是一個多線程收集器,也是使用復制演算法,但它的對象分配規則與回收策略都與ParNew收集器有所不同,它是以吞吐量最大化(即GC時間占總運行時間最小)為目標的收集器實現,它允許較長時間的STW換取總吞吐量最大化。4.Serial Old收集器Serial Old是單線程收集器,使用標記-整理演算法,是老年代的收集器,上面三種都是使用在新生代收集器。5.Parallel Old收集器老年代版本吞吐量優先收集器,使用多線程和標記-整理演算法,JVM 1.6提供,在此之前,新生代使用了PS收集器的話,老年代除Serial Old外別無選擇,因為PS無法與CMS收集器配合工作。6.CMS(Concurrent Mark Sweep)收集器CMS是一種以最短停頓時間為目標的收集器,使用CMS並不能達到GC效率最高(總體GC時間最小),但它能盡可能降低GC時服務的停頓時間,這一點對於實時或者高交互性應用(譬如證券交易)來說至關重要,這類應用對於長時間STW一般是不可容忍的。CMS收集器使用的是標記-清除演算法,也就是說它在運行期間會產生空間碎片,所以虛擬機提供了參數開啟CMS收集結束後再進行一次內存壓縮。
⑤ 如何使用Unsafe操作內存中的Java類和對象
本文由ImportNew-吳際翻譯自zeroturnaround。歡迎加入翻譯小組。轉載請參見文章末尾的要求。讓我們開始展示內存中Java類和對象結構你可曾好奇過Java內存管理核心構件?你是否問過自己某些奇怪的問題,比如:一個類在內存中占據多少空間?我的對象在內存中消耗了多少空間?對象的屬性在內存中是如何被布局的?如果這些問題聽起來很熟悉,那麼你就想到了點子上。對於像我們這樣的在RebelLabs的Java極客來說,這些難解的謎題已經在我們腦海中纏繞了很長時間:如果你對探究類檢測器感興趣,想知道如何布局讓所有的類更容易地從內存中取到指定變數,或是想在系統運行時侵入內存中的這些欄位。這就意味著你能切實改變內存中的數據甚至是代碼!其它可能勾起你興趣的知識點有,「堆外緩存」和「高性能序列化」的實現。這是一對構建在對象緩存結構上很好的實例,揭示了獲取類和實例內存地址的方法,緩存中類和實例的布局以及關於對象成員變數布局的詳細解釋。我們希望盡可能簡單地闡釋這些內容,但是盡管如此這篇文章並不適合Java初學者,它要求具備對Java編程原理有一定的了解。注意:下面關於類和對象的布局所寫的內容特指JavaSE7,所以不推薦使用者想當然地認為這些適用於過去或將來的Java版本。方便起見,我們在GitHub項目上發布了這篇文章的示例代碼,可以在這里找到。在Java中最直接的內存操作方法是什麼?Java最初被設計為一種安全的受控環境。盡管如此,JavaHotSpot還是包含了一個「後門」,提供了一些可以直接操控內存和線程的低層次操作。這個後門類——sun.misc.Unsafe——被JDK廣泛用於自己的包中,如java.nio和java.util.concurrent。但是絲毫不建議在生產環境中使用這個後門。因為這個API十分不安全、不輕便、而且不穩定。這個不安全的類提供了一個觀察HotSpotJVM內部結構並且可以對其進行修改。有時它可以被用來在不適用C++調試的情況下學習虛擬機內部結構,有時也可以被拿來做性能監控和開發工具。為何變得不安全sun.misc.Unsafe這個類是如此地不安全,以至於JDK開發者增加了很多特殊限制來訪問它。它的構造器是私有的,工廠方法getUnsafe()的調用器只能被Bootloader載入。如你在下面代碼片段的第8行所見,這個傢伙甚至沒有被任何類載入器載入,所以它的類載入器是null。它會拋出SecurityException異常來阻止侵入者。publicfinalclassUnsafe{privateUnsafe(){}=newUnsafe();publicstaticUnsafegetUnsafe(){Classcc=sun.reflect.Reflection.getCallerClass(2);if(cc.getClassLoader()!=null)thrownewSecurityException("Unsafe");returntheUnsafe;}}幸運的是這里有一個Unsafe的變數可以被用來取得Unsafe的實例。我們可以輕松地編寫一個復制方法通過反射來實現,如下所示:()publicstaticUnsafegetUnsafe(){try{Fieldf=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);return(Unsafe)f.get(null);}catch(Exceptione){/**/}}Unsafe一些有用的特性虛擬機「集約化」(VMintrinsification):如用於無鎖Hash表中的CAS(比較和交換)。再比如compareAndSwapInt這個方法用JNI調用,包含了對CAS有特殊引導的本地代碼。在這里你能讀到關於CAS的信息:。主機虛擬機(譯註:主機虛擬機主要用來管理其他虛擬機。而虛擬平台我們看到只有guestVM)的sun.misc.Unsafe功能能夠被用於未初始化的對象分配內存(用allocateInstance方法),然後將構造器調用解釋為其他方法的調用。你可以從本地內存地址中追蹤到這些數據。使用java.lang.Unsafe類獲取內存地址是可能的。而且可以通過unsafe方法直接操作這些變數!使用allocateMemory方法,內存可以被分配到堆外。例如當allocateDirect方法被調用時DirectByteBuffer構造器內部會使用allocateMemory。arrayBaseOffset和arrayIndexScale方法可以被用於開發arraylets,一種用來將大數組分解為小對象、限制掃描的實時消耗或者在大對象上做更新和移動。
⑥ C#代碼unsafe public static extern string shibie1(byte* ptr, int w, int h);轉成java代碼應該怎麼轉
你肯定是C# 代碼,C#有指針了??
PS:unsafe 的 還有地址操作,還是按功能重新吧。否則,不涉及地址操作,直接按代碼用 java 類重寫。
⑦ unsafe java 源碼怎麼查看
sun.misc.Unsafe至少從2004年Java1.4開始就存在於Java中了。在Java9中,為了提高JVM的可維護性,Unsafe和許多其他的東西一起都被作為內部使用類隱藏起來了。但是究竟是什麼取代Unsafe不得而知,個人推測會有不止一樣來取代它
⑧ java學習,根據要求創建相同(存儲地址相同)的對象
第一步驟:解決方式直接復制就可以了。
publicclassTest6{
publicstaticvoidmain(String[]args){
Personperson1=newPerson(1,"Tom",21);
Personperson2=person1;
System.out.println(person1.hashCode()+""+person2.hashCode());
System.out.println(person1.equals(person2));
System.out.println(person2==person1);
}
}
⑨ 如何獲得java對象的內存地址
在java中內存中的對象地址是可變的,所以獲得的內存地址有可能會變化。要獲得內存地址也只能通過Unsafe的方法來獲得,如下代碼示例:
packagecom.bijian.study;
importjava.lang.reflect.Field;
importsun.misc.Unsafe;
publicclassAddresser{
//實例化Unsafe 類
privatestaticUnsafeunsafe;
static{
try{
//得到field對象
Fieldfield=Unsafe.class.getDeclaredField("theUnsafe");
//設置獲取地址
field.setAccessible(true);
unsafe=(Unsafe)field.get(null);
}catch(Exceptione){
e.printStackTrace();
}
}
publicstaticlongaddressOf(Objecto)throwsException{
Object[]array=newObject[]{o};
longbaseOffset=unsafe.arrayBaseOffset(Object[].class);
intaddressSize=unsafe.addressSize();
longobjectAddress;
switch(addressSize){
case4:
objectAddress=unsafe.getInt(array,baseOffset);
break;
case8:
objectAddress=unsafe.getLong(array,baseOffset);
break;
default:
thrownewError("unsupportedaddresssize:"+addressSize);
}
return(objectAddress);
}
//列印地址的長度
publicstaticvoidmain(String...args)throwsException{
Objectmine="Hithere".toCharArray();
longaddress=addressOf(mine);
System.out.println("Addess:"+address);
//Verifyaddressworks-
printBytes(address,27);
}
//調用此方法得到地址
publicstaticvoidprintBytes(longobjectAddress,intnum){
//循環列印得到的地址。
for(longi=0;i<num;i++){
intcur=unsafe.getByte(objectAddress+i);
System.out.print((char)cur);
}
System.out.println();
}
}
運行結果:
⑩ Java為什麼會引入及如何使用Unsafe
sun.misc.Unsafe類提供了很多Native本地方法,通過這個類你可以直接進行內存,cpu相關的底層調用操作:
例如:
通過allocateInstance()方法,你可以創建一個類的實例,但是卻不需要調用它的構造函數、初使化代碼、各種JVM安全檢查以及其它的一些底層的東西。
使用objectFieldOffset方法我們可以實現C語言式的sizeOf功能。這個實現返回了對象的淺大小(shallow size )。
使用Unsafe. compareAndSwap*方法可以通過調用C++程序實現cpu級別的同步。(CAS)
java中的java.util.concurrent包都是通過Unsafe的compareAndSwap*方法實現的。
但這個類默認是不被允許創建實例的:
1 構造函數私有。 2 只有JDK信任的類才可以調用Unsafe.getUnsafe() 方法獲取實例。
雖然正常情況下無法調用,但我們可以通過反射來調用。
Fieldf=Unsafe.class.getDeclaredField(theUnsafe);
f.setAccessible(true);
Unsafeunsafe=(Unsafe)f.get(null);
這樣Eclipse會報錯,但是這樣就可以直接運行了。