摘要:對象頭的另外一部分是類型指針,即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。并不是所有的虛擬機實現(xiàn)都必須在對象數(shù)據(jù)上保留類型指針,換句話說,查找對象的元數(shù)據(jù)信息并不一定要經(jīng)過對象本身,這點將在節(jié)討論。
目錄介紹
1.關(guān)于int和Integer的問題區(qū)別分析
2.Integer的值緩存的原理
2.1 Java 5 中引入緩存特性
2.2 Integer類中的IntegerCache類
2.3 其他整型類型的緩存機制
3.理解自動裝箱和拆箱
3.1 什么是裝箱?什么是拆箱?
3.2 裝箱和拆箱是如何實現(xiàn)的
3.3 裝箱和拆箱在編程實際中注意點
4.原始類型線程安全問題
4.1 那些類型是線程安全的
4.2 如何驗證int類型是否線程安全
4.3 AtomicInteger線程安全版
5.Java 原始數(shù)據(jù)類型和引用類型局限性
5.1 原始數(shù)據(jù)類型和 Java 泛型并不能配合使用
5.2 無法高效地表達數(shù)據(jù),也不便于表達復雜的數(shù)據(jù)結(jié)構(gòu)
6.關(guān)于其他知識延伸
6.1 對象的內(nèi)存結(jié)構(gòu)
6.2 對象頭的結(jié)構(gòu)
6.3 如何計算或者獲取某個Java對象的大小
7.關(guān)于其他內(nèi)容介紹
7.1 關(guān)于博客匯總鏈接
7.2 關(guān)于我的博客
1.關(guān)于int和Integer的問題區(qū)別分析1.1 編譯階段、運行時,自動裝箱 / 自動拆箱是發(fā)生在什么階段?
1.2使用靜態(tài)工廠方法 valueOf 會使用到緩存機制,那么自動裝箱的時候,緩存機制起作用嗎?
1.3為什么我們需要原始數(shù)據(jù)類型,Java 的對象似乎也很高效,應用中具體會產(chǎn)生哪些差異?
1.4 閱讀過 Integer 源碼嗎?分析下類或某些方法的設(shè)計要點?
1.5 int和Integer的區(qū)別
1、Integer是int的包裝類,int則是java的一種基本數(shù)據(jù)類型 2、Integer變量必須實例化后才能使用,而int變量不需要 3、Integer實際是對象的引用,當new一個Integer時,實際上是生成一個指針指向此對象;而int則是直接存儲數(shù)據(jù)值 4、Integer的默認值是null,int的默認值是0 延伸: 關(guān)于Integer和int的比較 1、由于Integer變量實際上是對一個Integer對象的引用,所以兩個通過new生成的Integer變量永遠是不相等的(因為new生成的是兩個對象,其內(nèi)存地址不同)。 Integer i = new Integer(100); Integer j = new Integer(100); System.out.print(i == j); //false 2、Integer變量和int變量比較時,只要兩個變量的值是向等的,則結(jié)果為true(因為包裝類Integer和基本數(shù)據(jù)類型int比較時,java會自動拆包裝為int,然后進行比較,實際上就變?yōu)閮蓚€int變量的比較) Integer i = new Integer(100); int j = 100; System.out.print(i == j); //true 3、非new生成的Integer變量和new Integer()生成的變量比較時,結(jié)果為false。(因為非new生成的Integer變量指向的是java常量池中的對象,而new Integer()生成的變量指向堆中新建的對象,兩者在內(nèi)存中的地址不同) Integer i = new Integer(100); Integer j = 100; System.out.print(i == j); //false 4、對于兩個非new生成的Integer對象,進行比較時,如果兩個變量的值在區(qū)間-128到127之間,則比較結(jié)果為true,如果兩個變量的值不在此區(qū)間,則比較結(jié)果為false Integer i = 100; Integer j = 100; System.out.print(i == j); //true Integer i = 128; Integer j = 128; System.out.print(i == j); //false 對于第4條的原因: java在編譯Integer i = 100 ;時,會翻譯成為Integer i = Integer.valueOf(100);,而java API中對Integer類型的valueOf的定義如下: public static Integer valueOf(int i){ assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high){ return IntegerCache.cache[i + (-IntegerCache.low)]; } return new Integer(i); } java對于-128到127之間的數(shù),會進行緩存,Integer i = 127時,會將127進行緩存,下次再寫Integer j = 127時,就會直接從緩存中取,就不會new了2.Integer的值緩存的原理 2.1 Java 5 中引入緩存特性
在 Java 5 中,為 Integer 的操作引入了一個新的特性,用來節(jié)省內(nèi)存和提高性能。整型對象在內(nèi)部實現(xiàn)中通過使用相同的對象引用實現(xiàn)了緩存和重用。
這種 Integer 緩存策略僅在自動裝箱(autoboxing)的時候有用,使用構(gòu)造器創(chuàng)建的 Integer 對象不能被緩存。
2.2 Integer類中的IntegerCache類在創(chuàng)建新的 Integer 對象之前會先在 IntegerCache.cache (是個Integer類型的數(shù)組)中查找。有一個專門的 Java 類來負責 Integer 的緩存。
這個類是用來實現(xiàn)緩存支持,并支持 -128 到 127 之間的自動裝箱過程。最大值 127 可以通過 JVM 的啟動參數(shù) -XX:AutoBoxCacheMax=size 修改。 緩存通過一個 for 循環(huán)實現(xiàn)。從小到大的創(chuàng)建盡可能多的整數(shù)并存儲在一個名為 cache 的整數(shù)數(shù)組中。這個緩存會在 Integer 類第一次被使用的時候被初始化出來。以后,就可以使用緩存中包含的實例對象,而不是創(chuàng)建一個新的實例(在自動裝箱的情況下)。
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }2.3 其他整型類型的緩存機制
這種緩存行為不僅適用于Integer對象。我們針對所有整數(shù)類型的類都有類似的緩存機制。
有 ByteCache 用于緩存 Byte 對象
有 ShortCache 用于緩存 Short 對象
有 LongCache 用于緩存 Long 對象
有 CharacterCache 用于緩存 Character 對象
Byte,Short,Long 有固定范圍: -128 到 127。對于 Character, 范圍是 0 到 127。除了 Integer 可以通過參數(shù)改變范圍外,其它的都不行。
3.理解自動裝箱和拆箱 3.1 什么是裝箱?什么是拆箱?裝箱就是 自動將基本數(shù)據(jù)類型轉(zhuǎn)換為包裝器類型;拆箱就是 自動將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型。
//拆箱 int yc = 5; //裝箱 Integer yc = 5;3.2 裝箱和拆箱是如何實現(xiàn)的
以Interger類為例,下面看一段代碼來了解裝箱和拆箱的實現(xiàn)
public class Main { public static void main(String[] args) { Integer y = 10; int c = i; } }
然后來編譯一下,看下圖所示:
從反編譯得到的字節(jié)碼內(nèi)容可以看出,在裝箱的時候自動調(diào)用的是Integer的valueOf(int)方法。而在拆箱的時候自動調(diào)用的是Integer的intValue方法。
因此可以用一句話總結(jié)裝箱和拆箱的實現(xiàn)過程:裝箱過程是通過調(diào)用包裝器的valueOf方法實現(xiàn)的,而拆箱過程是通過調(diào)用包裝器的 xxxValue方法實現(xiàn)的。(xxx代表對應的基本數(shù)據(jù)類型)。
3.3 裝箱和拆箱在編程實際中注意點建議避免無意中的裝箱、拆箱行為,尤其是在性能敏感的場合,創(chuàng)建 10 萬個 Java 對象和 10 萬個整數(shù)的開銷可不是一個數(shù)量級的,不管是內(nèi)存使用還是處理速度,光是對象頭的空間占用就已經(jīng)是數(shù)量級的差距了。
4.原始類型線程安全問題 4.1 那些類型是線程安全的Java自帶的線程安全的基本類型包括: AtomicInteger, AtomicLong, AtomicBoolean, AtomicIntegerArray,AtomicLongArray等
4.2 如何驗證int類型是否線程安全200個線程,每個線程對共享變量 count 進行 50 次 ++ 操作
int 作為基本類型,直接存儲在內(nèi)存棧,且對其進行+,-操作以及++,–操作都不是原子操作,都有可能被其他線程搶斷,所以不是線程安全。int 用于單線程變量存取,開銷小,速度快
int count = 0; private void startThread() { for (int i = 0;i < 200; i++){ new Thread(new Runnable() { @Override public void run() { for (int k = 0; k < 50; k++){ count++; } } }).start(); } // 休眠10秒,以確保線程都已啟動 try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally { Log.e("打印日志----",count+""); } } //期望輸出10000,最后輸出的是9818 //注意:打印日志----: 98184.3 AtomicInteger線程安全版
AtomicInteger類中有有一個變量valueOffset,用來描述AtomicInteger類中value的內(nèi)存位置 。
當需要變量的值改變的時候,先通過get()得到valueOffset位置的值,也即當前value的值.給該值進行增加,并賦給next
compareAndSet()比較之前取到的value的值當前有沒有改變,若沒有改變的話,就將next的值賦給value,倘若和之前的值相比的話發(fā)生變化的話,則重新一次循環(huán),直到存取成功,通過這樣的方式能夠保證該變量是線程安全的
value使用了volatile關(guān)鍵字,使得多個線程可以共享變量,使用volatile將使得VM優(yōu)化失去作用,在線程數(shù)特別大時,效率會較低。
private static AtomicInteger atomicInteger = new AtomicInteger(1); static Integer count1 = Integer.valueOf(0); private void startThread1() { for (int i = 0;i < 200; i++){ new Thread(new Runnable() { @Override public void run() { for (int k = 0; k < 50; k++){ // getAndIncrement: 先獲得值,再自增1,返回值為自增前的值 count1 = atomicInteger.getAndIncrement(); } } }).start(); } // 休眠10秒,以確保線程都已啟動 try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally { Log.e("打印日志----",count1+""); } } //期望輸出10000,最后輸出的是10000 //注意:打印日志----: 10000 //AtomicInteger使用了volatile關(guān)鍵字進行修飾,使得該類可以滿足線程安全。 private volatile int value; /** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ public AtomicInteger(int initialValue) { value = initialValue; }5.Java 原始數(shù)據(jù)類型和引用類型局限性 5.1 原始數(shù)據(jù)類型和 Java 泛型并不能配合使用
Java 的泛型某種程度上可以算作偽泛型,它完全是一種編譯期的技巧,Java 編譯期會自動將類型轉(zhuǎn)換為對應的特定類型,這就決定了使用泛型,必須保證相應類型可以轉(zhuǎn)換為Object。
5.2 無法高效地表達數(shù)據(jù),也不便于表達復雜的數(shù)據(jù)結(jié)構(gòu)Java 的對象都是引用類型,如果是一個原始數(shù)據(jù)類型數(shù)組,它在內(nèi)存里是一段連續(xù)的內(nèi)存,而對象數(shù)組則不然,數(shù)據(jù)存儲的是引用,對象往往是分散地存儲在堆的不同位置。這種設(shè)計雖然帶來了極大靈活性,但是也導致了數(shù)據(jù)操作的低效,尤其是無法充分利用現(xiàn)代 CPU 緩存機制。
Java 為對象內(nèi)建了各種多態(tài)、線程安全等方面的支持,但這不是所有場合的需求,尤其是數(shù)據(jù)處理重要性日益提高,更加高密度的值類型是非?,F(xiàn)實的需求。
6.關(guān)于其他知識延伸 6.1 對象的內(nèi)存結(jié)構(gòu)對象在內(nèi)存中存儲的布局可以分為3塊區(qū)域:對象頭(Header)、實例數(shù)據(jù)(Instance Data)和對齊填充(Padding)。
6.2 對象頭的結(jié)構(gòu)HotSpot虛擬機的對象頭包括兩部分信息,第一部分用于存儲對象自身的運行時數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標志、線程持有的鎖、偏向線程ID、偏向時間戳等,這部分數(shù)據(jù)的長度在32位和64位的虛擬機(未開啟壓縮指針)中分別為32bit和64bit,官方稱它為"Mark Word"。
對象頭的另外一部分是類型指針,即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。并不是所有的虛擬機實現(xiàn)都必須在對象數(shù)據(jù)上保留類型指針,換句話說,查找對象的元數(shù)據(jù)信息并不一定要經(jīng)過對象本身,這點將在2.3.3節(jié)討論。另外,如果對象是一個Java數(shù)組,那在對象頭中還必須有一塊用于記錄數(shù)組長度的數(shù)據(jù),因為虛擬機可以通過普通Java對象的元數(shù)據(jù)信息確定Java對象的大小,但是從數(shù)組的元數(shù)據(jù)中卻無法確定數(shù)組的大小。
6.3 如何計算或者獲取某個Java對象的大小獲取一個JAVA對象的大小,可以將一個對象進行序列化為二進制的Byte,便可以查看大小
//獲取一個JAVA對象的大小,可以將一個對象進行序列化為二進制的Byte,便可以查看大小 Integer value = 10; ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos ; try { oos = new ObjectOutputStream(bos); oos.writeObject(value); oos.close(); } catch (IOException e) { e.printStackTrace(); } // 讀出當前對象的二進制流信息 Log.e("打印日志----",bos.size()+""); //打印日志----: 817.關(guān)于其他內(nèi)容介紹 7.1 關(guān)于博客匯總鏈接
1.技術(shù)博客匯總
2.開源項目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
7.2 關(guān)于我的博客我的個人站點:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...
郵箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/97088.html
摘要:但是,如果像上例中只取最后幾位的時候,這可不是什么好事,即使我的數(shù)據(jù)分布很散亂,但是哈希沖突仍然會很嚴重。由于我們所創(chuàng)建的是類型的,這也是最巧的一點,類型的返回值就是其值本身,而存儲的時候元素通過一些運算后會得出自己在數(shù)組中所處的位置。 HashSet 是否無序 (一) 問題起因: 《Core Java Volume I—Fundamentals》中對HashSet的描述是這樣的: H...
摘要:本章部分內(nèi)容從源碼中解讀一些自動裝箱與拆箱的原理,以及會出現(xiàn)的一些陷阱已經(jīng)性能等。例題分析我們通過幾個經(jīng)典的問題,來看看大家到底理解了裝箱與拆箱的知識點沒。 showImg(https://img-blog.csdnimg.cn/20190426221838971.gif);showImg(https://img-blog.csdnimg.cn/20190426221918208.pn...
摘要:接下來看下如果使用提供的接口會有哪些改進首先看下接口定義省略該函數(shù)式接口唯一的抽象方法接收一個參數(shù),有返回值。是不是有點體驗到函數(shù)式編程的靈活之處。 上一篇文章中,我們總體介紹了創(chuàng)建函數(shù)式接口實例的幾種方式以及Java8中接口新增的默認方法特性,接下來我們來看下Java8中已經(jīng)為我們提供的幾種典型的函數(shù)式接口先看一個示例 public class FunctionTest { ...
摘要:相等判斷符介紹相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù)當比較基本數(shù)據(jù)類型的時候比較的是數(shù)值當比較引用類型數(shù)據(jù)時比較的是引用指針判斷基本類型是否相等首先基本數(shù)據(jù)類型指的是中的八大數(shù)據(jù)類型這八大基本數(shù)據(jù)類型有個共同的特點是它們在內(nèi)存中是有具相等判斷符==介紹 ? ==相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù). 當比較基本數(shù)據(jù)類型的時候比較的是數(shù)值, 當比較引用類型數(shù)據(jù)時比較的是引用(指...
摘要:相等判斷符介紹相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù)當比較基本數(shù)據(jù)類型的時候比較的是數(shù)值當比較引用類型數(shù)據(jù)時比較的是引用指針判斷基本類型是否相等首先基本數(shù)據(jù)類型指的是中的八大數(shù)據(jù)類型這八大基本數(shù)據(jù)類型有個共同的特點是它們在內(nèi)存中是有具相等判斷符==介紹 ==相等判斷符用于比較基本數(shù)據(jù)類型和引用類型數(shù)據(jù). 當比較基本數(shù)據(jù)類型的時候比較的是數(shù)值, 當比較引用類型數(shù)據(jù)時比較的是引用(指針...
閱讀 1613·2021-11-04 16:10
閱讀 3014·2021-09-30 09:48
閱讀 2899·2019-08-29 11:31
閱讀 1639·2019-08-28 18:22
閱讀 3289·2019-08-26 13:44
閱讀 1379·2019-08-26 13:42
閱讀 2909·2019-08-26 10:20
閱讀 821·2019-08-23 17:00