java中一半是天使一半是魔鬼的Unsafe類詳解 - 愚公要移山1

2020-12-20 愚公要移山1

可能我們會奇怪,java中竟然給一個類起名字叫做「不安全」。慢慢看,你就會發現這個類的神奇之處,雖然功能很強大,但是的確不那麼安全。

一、簡單介紹

首先在Oracle的Jdk8無法獲取到sun.misc包的源碼,想看此包的源碼可以直接下載openjdk。

1、預備工作

openjdk的源碼我下載的是openjdk-8u40-src-b25-10_feb_2015,有需要的可以私信我,如果是我公眾號粉絲,我會直接附加上這個百度雲資源。在下載完成之後,然後就可以直接導入我們的eclipse了。

windows->preference->installed jres->選中jre->edit->rt.jar->source attachment->external folders->openjdk源碼路徑。此時就可以查看我們的Unsafe類的源碼了。

2、簡介說明

如果你學習了一些java並發包裡面的類源碼的話,對這個Unsafe類一定不陌生,整個java並發包底層實現的核心就是它,在很久之前盛傳著這個類將要在jdk9移除,實時上如果移除了那麼一大批框架將會消失,比如說赫赫有名的Netty框架。最終jdk9出現的時候也只是對其進行了改進和優化。不過這也再一次說明了這個類的重要地位。

為什麼說它一半是天使一半是魔鬼呢?要回答這個問題,我們還是要從其特性來解釋。

Unsafe類使Java擁有了像C語言的指針一樣操作內存空間的能力,一旦能夠直接操作內存,這也就意味著(1)不受jvm管理,也就意味著無法被GC,需要我們手動GC,稍有不慎就會出現內存洩漏。(2)Unsafe的不少方法中必須提供原始地址(內存地址)和被替換對象的地址,偏移量要自己計算,一旦出現問題就是JVM崩潰級別的異常,會導致整個JVM實例崩潰,表現為應用程式直接crash掉。(3)直接操作內存,也意味著其速度更快,在高並發的條件之下能夠很好地提高效率。

因此,從上面三個角度來看,雖然在一定程度上提升了效率但是也帶來了指針的不安全性。

下面我們深入到源碼中看看,提供了什麼方法直接操作內存。

二、源碼分析

Unsafe中一共有82個public native修飾的方法,還有幾十個基於這82個public native方法的其他方法。這些方法大體可以歸結為以下幾類:

(1)初始化操作

(2)操作對象屬性

(3)操作數組元素

(4)線程掛起和回復

(5)CAS機制

下面我們對這些方法盡進行一個大致的分析。

1、初始化

在這裡我們看到Unsafe的初始化方法主要是通過getUnsafe方法的單例模式實現的,調用JVM本地方法registerNatives()和sun.reflect.Reflection,通過Reflection的getCallerClass判斷當前調用的類是否是主類加載器(BootStrap classLoader)加載的,否則的話拋出一個SecurityException。這也證明了一個問題,那就是只有由主類加載器(BootStrap classLoader)加載的類才能調用這個類中的方法。

2、操作屬性方法

(1)public native Object getObject(Object o, long offset);

通過給定的Java變量獲取引用值。這裡實際上是獲取一個Java對象o中,獲取偏移地址為offset的屬性的值,此方法可以突破修飾符的抑制,也就是無視private、protected和default修飾符。類似的方法有getInt、getDouble等等。同理還有putObject方法。

(2)public native Object getObjectVolatile(Object o, long offset);

強制從主存中獲取屬性值。類似的方法有getIntVolatile、getDoubleVolatile等等。同理還有putObjectVolatile。

(3)public native void putOrderedObject(Object o, long offset, Object x);

設置o對象中offset偏移地址offset對應的Object型field的值為指定值x。這是一個有序或者有延遲的putObjectVolatile方法,並且不保證值的改變被其他線程立即看到。只有在field被volatile修飾並且期望被修改的時候使用才會生效。類似的方法有putOrderedInt和putOrderedLong。

(4)public native long staticFieldOffset(Field f);

返回給定的靜態屬性在它的類的存儲分配中的位置(偏移地址)。

(5)public native long objectFieldOffset(Field f);

返回給定的非靜態屬性在它的類的存儲分配中的位置(偏移地址)。

(6)public native Object staticFieldBase(Field f);

返回給定的靜態屬性的位置,配合staticFieldOffset方法使用。

3、操作數組

(1)public native int arrayBaseOffset(Class arrayClass);

返回數組類型的第一個元素的偏移地址(基礎偏移地址)。

(2)public native int arrayIndexScale(Class arrayClass);

返回數組中元素與元素之間的偏移地址的增量。

這兩個方法配合使用就可以定位到任何一個元素的地址。

4、內存管理

(1)public native int addressSize();

獲取本地指針的大小(單位是byte),通常值為4或者8。常量ADDRESS_SIZE就是調用此方法。

(2)public native int pageSize();

獲取本地內存的頁數,此值為2的冪次方。

(3)public native long allocateMemory(long bytes);

分配一塊新的本地內存,通過bytes指定內存塊的大小(單位是byte),返回新開闢的內存的地址。

(4)public native long reallocateMemory(long address, long bytes);

通過指定的內存地址address重新調整本地內存塊的大小,調整後的內存塊大小通過bytes指定(單位為byte)。

(5)public native void setMemory(Object o, long offset, long bytes, byte value);

將給定內存塊中的所有字節設置為固定值(通常是0)。

5、線程掛起和恢復

(1)public native void unpark(Object thread);

釋放被park創建的在一個線程上的阻塞。由於其不安全性,因此必須保證線程是存活的。

(2)public native void park(boolean isAbsolute, long time);`

阻塞當前線程,一直等道unpark方法被調用。

6、內存屏障

(1)public native void loadFence();

在該方法之前的所有讀操作,一定在load屏障之前執行完成。

(2)public native void storeFence();

在該方法之前的所有寫操作,一定在store屏障之前執行完成

(3)public native void fullFence();

在該方法之前的所有讀寫操作,一定在full屏障之前執行完成,這個內存屏障相當於上面兩個(load屏障和store屏障)的合體功能。

7、CAS機制

這個Unsafe類其實是貫穿到整個java並發包體系中的,不管是你看原子包還是lock包底部都有這樣的一個類,我們需要記住的不是每一個方法,而是上面七類的標題。也就是具體有什麼功能。

三、使用

說了這麼久的源碼在這裡才介紹其使用,是因為官方並不推薦我們使用,也就是說我們無法直接new出來一個Unsafe類出來,那我們該如何使用呢?在很久之前我曾寫過一篇介紹java反射機制的文章,沒錯就是這個反射機制,牛的不行。Unsafe就可以通過反射機制來獲取。

在這裡只是給出一些簡單的例子,其用法可以參照源碼分析中那七個方向。

仔細閱讀jdk的源碼有時候真的可以收穫一些意想不到的東西。感謝大家支持。

相關焦點

  • 摩羯座一半是天使,一半是魔鬼
    摩羯座的由來早在5000年前,就已經有了摩羯座,即海中之豚的星象描述。摩羯座亮星組成一個倒三角形結構,古人將其稱為「神仙之門」,認為從地上各種名利是非解脫出來的人,其靈魂就可以通過此門登上天國。在希臘神話中,他管著宙斯的牛羊,他們都叫他牧神潘恩。潘恩長得十分醜陋,幾乎可以用猙獰來形容。頭上生了兩支角,而下半身該是腳的部分卻是一支羊蹄。這樣醜陋的外表,讓牧神潘恩十分難堪與自卑,不能隨著眾神歌唱,不能向翩翩的仙子求愛。啊!誰能了解醜陋的外表之下,也有一顆熱情奔放的心?日日夜夜,他只能藉著吹蕭來抒解心中的悲苦。
  • 一半天使一半魔鬼馬拉度納1986年的奇蹟
    一半天使一半魔鬼馬拉度納1986年的奇蹟 2020-11-26 06:00  ETtoday新聞雲
  • 阿泰是一半天使一半魔鬼,鮑文卻沒有一點天使的影子
    形容NBA的退役球員阿泰斯特(慈善世界和平),小編覺得是一半天使一半魔鬼非常貼切的。那布魯斯·鮑文該如何形容了?據爆料,鮑文連續3年在最佳防守球員的評選中名列第二,但是都沒有獲得最佳防守球員,據爆料,NBA聯盟也覺得鮑文的防守確實有點髒,不願意讓這樣的一個人成為DPOY,因為那樣會給球迷們樹立不好的榜樣。
  • 為什麼Java中序列化的SerialVersionUID總是無意義的?
    下面我們舉一個例子:(1)不指定serialVersionUID首先我們定義一個User類,繼承Serializable接口OK,這就是java中這個serialVersionUID的作用,其實就是給這個類添加一個身份ID,進行在序列化之前和之後進行版本的比對。上面這個其實也是一個面試常問的一個問題,再次湊巧給總結了一下,不過今天的主題不是講這個serialVersionUID的,而是後面的那一串數字為什麼總是無意義的?二、為什麼總是無意義的ID?
  • Java中的Unsafe
    Java中的Unsafe類為我們提供了類似C++手動管理內存的能力。Unsafe類,全限定名是sun.misc.Unsafe,從名字中我們可以看出來這個類對普通程式設計師來說是「危險」的,一般應用開發者不會用到這個類。Unsafe類是"final"的,不允許繼承。
  • 一半一半!
    01友誼,一半是牽掛,一半是忘記朋友不一定要經常聯繫,但肯定不會忘記對方。偶爾想起對方,心中還是會泛起一絲絲的牽掛。即使時光飛逝,可我們的友誼永不會改變。相信孩子,靜待花開,也許你的種子永遠不會開花,因為從一開始,他就註定是要成為一棵參天大樹!04工作,一半是運氣,一半是努力每個人大多數時間都是在工作,工作不僅是養家餬口的保障,也是磨練意志,走向更好人生的臺階。
  • 一半一半
    相信孩子,靜待花開,也許你的種子永遠不會開花,因為從一開始,他就註定是要成為一棵參天大樹! 04 工作,一半是運氣,一半是努力。 05 金錢,一半是天使,一半是魔鬼。金錢拯救生命時,它是天使;人為金錢喪命時,它是魔鬼;滿足正常需求時,它是天使;膨脹私慾妄念時,它是魔鬼;君子愛財,取之有道。金錢本身沒有罪惡,只是人類對金錢的貪婪是所有罪惡的來源。
  • 【java基礎】並發包unsafe類概述
    Unsafe類介紹第一次看到這個類時被它的名字嚇到了,居然還有一個類名Unsafe...讀完本文,大家也能發現Unsafe類確實有點不那麼安全,
  • 你了解java中的幾種編碼方式?解決亂碼問題可能並不麻煩
    前言編碼的轉換通常在IO機制中使用,一個好的編碼可以為我們節省很多空間,在某種程度上提高我們應用的效率。由於之前就知道String中的轉換方式,還有一些工具類,因此今天就好好的整理一下java中jdk提供的幾種轉換方式,希望對你有幫助。
  • 動漫中擁有一半天使翅膀,一半惡魔翅膀的角色
    在動漫中,總是有很多亦正亦邪的人物很受歡迎,他們一半是天使,一半是惡魔,無論哪一種,都同樣有魅力。特別是那種擁有一半天使翅膀一半惡魔翅膀的角色。而且,像這樣的設定似乎也註定了他們需要在這兩邊做出選擇。比如下面這幾位:1、司狼神威——《X戰紀》神威是這部作品的主角,十一位帥氣的高一學生。也是掌握世界命運的關鍵者。名字的含義是代行神之威嚴者和獵取神之威嚴者。有著能決定人類命運的力量。他可以做滅世的地龍,也可以做救世的天龍。這是他不願面對的宿命。
  • 田馥甄《魔鬼中的天使》,每個人的心裡都有一個魔鬼和天使
    可是這樣的愛情在現實中真的是少之又少,很多人在愛的時候轟轟烈烈,周圍的朋友都以為他們兩個肯定會結婚,但是突然有一天,兩個人毫無預兆的分手了。今天給大家分享的是來自田馥甄的歌曲《魔鬼中的天使》,這首歌收錄於其2011年發行的《My love》專輯中。這首歌講的是人的心裡都住著一半魔鬼和一半天使,所以這首歌的名字也叫做《魔鬼中的天使》,意思就是說你討厭他這一面,可是你卻又喜歡上他的另一面。
  • Unsafe函數是什麼?
    不過儘管如此,JVM 還是開了一個後門,JDK 中有一個類 Unsafe,底層是使用C/C++寫的,它提供了硬體級別的原子操作。Unsafe為我們提供了訪問底層的機制,這種機制僅供java核心類庫使用,而不應該被普通用戶使用。
  • 一半一半(寫的真好!)
    05 金錢,一半是天使,一半是魔鬼。金錢拯救生命時,它是天使;人為金錢喪命時,它是魔鬼;滿足正常需求時,它是天使;膨脹私慾妄念時,它是魔鬼;君子愛財,取之有道。金錢本身沒有罪惡,只是人類對金錢的貪婪是所有罪惡的來源。
  • 《閃耀暖暖》出現「惡魔」畫像,一半天使一半惡魔,真實身份驚人
    這張惡魔畫像,一半天使一半惡魔,吸引了在場所有人的目光,並且畫中人還是閃耀暖暖中的一名遊戲角色,這位神秘而充滿誘惑的惡魔,她的來歷也並不只是惡魔畫像,真實身份很驚人。閃耀暖暖官方這次發布的惡魔畫像,畫中人既有一半天使的面孔,也有惡魔的誘惑力,她還是閃耀暖暖中的一名NPC?
  • 一半「天使」,一半「魔鬼」,重慶80後護士的「雙面人生」……
    鮮為人知的是,80後的姚靜除了是一位經驗豐富的護士外,也是一位11歲兒子的母親,因為丈夫長期在西藏執勤,工作之餘,她還要照顧兒子,所以她不得不在友善的天使和嚴厲的「魔鬼」之間切換身份。在醫院上班的姚靜患者說她是「天使」市急救中心創傷外科經常會收治危重病人,因此,這裡的護士任務不僅僅是護理。
  • 蔡蘿莉挑戰黑白絲襪 一半天使一半惡魔!看到全身照後被驚豔了
    說起蔡蘿莉相信各位經常看直播的小夥伴們都很熟悉了,蘿莉面孔魔鬼身材,僅憑這兩點就讓她的直播圈粉無數,太合宅男們胃口啦!而關於她的各種八卦節奏也是從來都沒停過,不過對於粉絲們來說看她直播最重要還是看她的顏還有各種二次元的元素,這些似乎也沒那麼重要了。
  • 阿里面試官:你了解Java Unsafe類嗎?
    1、Unsafe 實例化  在使用Unsafe之前我們需要先實例化它。但我們不能通過像Unsafe unsafe = new Unsafe()這種簡單的方式來實現Unsafe的實例化,這是由於Unsafe的構造方法是私有的。
  • 老師,一半天使、一半惡魔
    老師,請您別一半天使一半惡魔打小到現在,在我心裡「老師」一直是一個神聖且受人尊敬的這麼一個職位,但凡我們能看到讀書時期寫關於老師的作文,都是很正面陽光廉潔的這麼一個形象。
  • 阿里面試官:你了解Java Unsafe類嗎?|java|字符串|調用|實例化|...
    1、Unsafe 實例化  在使用Unsafe之前我們需要先實例化它。但我們不能通過像Unsafe unsafe = new Unsafe()這種簡單的方式來實現Unsafe的實例化,這是由於Unsafe的構造方法是私有的。
  • 全唐詩抄12:一半是藝術,一半是世故,這會是什麼類型的詩?
    出自全唐詩卷32第29【背景大略】有道是:「一半是天使,一半是魔鬼」。詩歌裡卻也有,一半是精緻的藝術、一半是矯情的世故,這種神奇類型的存在。本期抄錄的這首初唐詩,就是一個鮮活的例子。酬和詩這一本質,決定了它一半藝術、一半世故的特點。先看「天使」,此詩的前半部分,在唐初算得上是寫景的藝術精品。晴天度旅雁,斜影照殘虹。野淨餘煙盡,山明遠色同。