自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

怎樣計算一個 Java 對象大???這兒有幾種方法

開發(fā) 后端
計算機(jī)發(fā)展到今天,硬件看似配置越來越高,但依然架不住你隨意寫,搞不好應(yīng)用就掛了。另外像游戲等一些行業(yè)還是「錙銖必較」,讓應(yīng)用能 穩(wěn)定的運(yùn)行。在Java 應(yīng)用里,要想精確計算,需要對于對象的占用大小做到心里有數(shù)。那這篇文章一起來看看, 在 Java 的世界里,一個對象的大小究竟是多少呢?有哪些方式能夠計算對象大小。

 [[319676]]

在計算機(jī)發(fā)展的早期階段,硬件的發(fā)展速度慢,容量小,所以軟件開發(fā)人員寫起代碼里對 byte、bit 都是「斤斤計較」,這才使用寫出來的應(yīng)用能在我們今天看起來那么小的,配置那么低的硬件中運(yùn)行良好,同時效果驚人。

那么計算機(jī)發(fā)展到今天,硬件看似配置越來越高,但依然架不住你隨意寫,搞不好應(yīng)用就掛了。另外像游戲等一些行業(yè)還是「錙銖必較」,讓應(yīng)用能 穩(wěn)定的運(yùn)行。在Java 應(yīng)用里,要想精確計算,需要對于對象的占用大小做到心里有數(shù)。那這篇文章一起來看看, 在 Java 的世界里,一個對象的大小究竟是多少呢?有哪些方式能夠計算對象大小。

要看一個對象的大小,首先需要看Java 運(yùn)行的平臺是 32位還是 64位的 ,其次還要看對象內(nèi)有多少屬性(field)。最后還有一些 JVM 自身需要 在對象里記錄的信息,比如說有GC的狀態(tài)、同步狀態(tài)、數(shù)組長度等等多種信息。這些項匯總求和,基本就是一個對象的大小了。不過VM為了效率,會采用固定長度,比如 8 位的整數(shù)倍來統(tǒng)一存儲。這種情況下,如果原對象大小不足時,就會擴(kuò)展對齊來存儲。

好的,下面來看下 JVM 里,一個Java 對象大小占用多少。

由于現(xiàn)在基本操作系統(tǒng)基本都是64位,咱們后面都以64位 JVM 來說明。

首先來看兩個例子:

  1. public class App { 

 

  1. public class App { 
  2.    private byte a; 
  3.    private int b; 

 

例子1里, App 這個對象占用內(nèi)存大小是多少 byte 呢?答案是 16。例子2 又是多少呢?答案是 24。

這里是怎么計算的呢?

計算方式

和開頭的文字描述類似,在Java 里一個對象大小,是由這些內(nèi)容組成

一個對象Object 大小 = Header + Primitive Fields + Reference Fields + Alignment & Padding`

其中的 Header 部分,就是 JVM 用于記錄特定信息的,一版也叫做 object header,在 OpenJDK 的匯總頁里,描述是這樣的:

Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object's layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.

我們看到 object header 由這些部分組成:

  • mark word
  • klass pointer
  • (Optinal) 如果是數(shù)組,會記錄數(shù)組的長度

mark word

The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

klass pointer

The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the "klass" contains a C++ style "vtable".

總結(jié)一下,對象頭里,基本是 GC的狀態(tài)、同步狀態(tài)、identity hash code,數(shù)組長度,以及 class 元信息的指針。

header 的長度由兩個 word 組成。mark word 在 64位VM里,長度是 8 bytes。klass pointer 的長度64位VM下受參數(shù)-XX:+UseCompressedOops配置控制, 可能是 4 bytes,也可能是8 bytes。

例子1,我們看關(guān)閉情況-XX:-UseCompressedOops

  1. OFFSET  SIZE   TYPE DESCRIPTION                               VALUE 
  2.       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1) 
  3.       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0) 
  4.       8     4        (object header)                           38 c4 6a 97 (00111000 11000100 01101010 10010111) (-1754610632) 
  5.      12     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1) 
  6. Instance size: 16 bytes 
  7. Space losses: 0 bytes internal + 0 bytes external = 0 bytes total 

16 bytes 全都是對象頭。

如果打開,則 object header 占 12 個bytes, 另外4個會補(bǔ)齊。

  1. OFFSET  SIZE   TYPE DESCRIPTION                               VALUE 
  2.       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1) 
  3.       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0) 
  4.       8     4        (object header)                           05 c0 00 f8 (00000101 11000000 00000000 11111000) (-134168571) 
  5.      12     4        (loss due to the next object alignment) 
  6. Instance size: 16 bytes 
  7. Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 

例子2,輸出是這樣:

  1. OFFSET  SIZE   TYPE DESCRIPTION                               VALUE 
  2.       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1) 
  3.       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0) 
  4.       8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315) 
  5.      12     4    int App.b                                     0 
  6.      16     1   byte App.a                                     0 
  7.      17     7        (loss due to the next object alignment) 
  8. Instance size: 24 bytes 
  9. Space losses: 0 bytes internal + 7 bytes external = 7 bytes total 

增加了 field 的占用, 這里的占用大小,就是咱們常說的 基本數(shù)據(jù)類型 的大小。如果有對象引用類型,就再加上這些的大小即可。

工具

下面的這些工具及方式,可用來計算對象的大小。

1.JOL

JOL 是 OpenJDK 提供的一個工具,在項目中添加依賴后可直接使用。

  1. <dependency> 
  2.     <groupId>org.openjdk.jol</groupId> 
  3.     <artifactId>jol-core</artifactId> 
  4.     <version>0.9</version> 
  5. </dependency> 

然后代碼里直接輸出即可。

  1. public class App { 
  2.     private byte a; 
  3.     private int b;     
  4.     public static void main(String[] args) { 
  5.         // 這里就會輸出和上面類似的內(nèi)容 
  6.         System.out.println(ClassLayout.parseClass(new App()).toPrintable());  
  7.     } 

2.Instrument

在 Instrument 包的 Instrumentation類內(nèi),有一個可以直接獲取對象大小的方法,注釋如下:

  1. /** 
  2.     * Returns an implementation-specific approximation of the amount of storage consumed by 
  3.     * the specified object. The result may include some or all of the object's overhead, 
  4.     * and thus is useful for comparison within an implementation but not between implementations. 
  5.     * 
  6.     * The estimate may change during a single invocation of the JVM. 
  7.     * 
  8.     * @param objectToSize     the object to size 
  9.     * @return an implementation-specific approximation of the amount of storage consumed by the specified object 
  10.     * @throws java.lang.NullPointerException if the supplied Object is <code>null</code>. 
  11.     */ 

那怎樣拿到 Instrument,從而調(diào)用這個方法,一般是通過 Agent attach 到 VM上來得到,一種是Agent 實現(xiàn)premain方法,一種是agentMain方法,區(qū)別在于attach的時機(jī)。

比如你自己定義了一個Agent 的 jar

  1. import java.lang.instrument.Instrumentation; 
  2.  
  3. public class ObjectSizeFetcher { 
  4.     private static Instrumentation instrumentation; 
  5.  
  6.     public static void premain(String args, Instrumentation inst) { 
  7.         instrumentation = inst; 
  8.     } 
  9.  
  10.     public static long getObjectSize(Object o) { 
  11.         return instrumentation.getObjectSize(o); 
  12.     } 

后面咱們在代碼里直接靜態(tài)調(diào)用 getObjectSize 方法就行了。

3.SA

我之前的文章(Java虛擬機(jī)的顯微鏡 Serviceability Agent)里介紹過 SA(Serviceablity Agent), 通過 SA,可以做到許多剖析 JVM 的事情。觀察對象的組成和大小當(dāng)然也不在話下。

通過SA來觀察上面例子2,效果是這樣的:

咱們能看到,整個對象先是_mark(mark word), 之后是 compressed klass(klass pointer ), 再后面是實例包含的屬性。 如果紅框所示,在 klass pointer 里面包含一個 _layout_helper,顯示的就是該對象的大小。

此外,在SA里的 Console 里,可以像命令行一樣交互,也可以顯示對象的大小。

PS: 對于對象的大小,JVM在初始化分配的時候,會對 field 有個「重排序」,給 field 的分配排序,從而節(jié)省空間。比如先分配byte 或者 boolean 之后,再分配 int 這些,那 byte 之后可能需要增加 3 bytes 進(jìn)行 padding,重排序可以減小空間占用。

比如有這些屬性:

  1. public class App { 
  2.     private byte a; 
  3.     private int b; 
  4.     private boolean c; 
  5.     private float d; 
  6.     private char e = 'a'
  1. OFFSET  SIZE      TYPE DESCRIPTION                               VALUE 
  2.       0     4           (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1) 
  3.       4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0) 
  4.       8     4           (object header)                           61 c1 00 f8 (01100001 11000001 00000000 11111000) (-134168223) 
  5.      12     4       int App.b                                     0 
  6.      16     4     float App.d                                     0.0 
  7.      20     2      char App.e                                     a 
  8.      22     1      byte App.a                                     0 
  9.      23     1   boolean App.c                                     false 
  10. Instance size: 24 bytes 
  11. Space losses: 0 bytes internal + 0 bytes external = 0 bytes total 

在輸出對象內(nèi)容,我們發(fā)現(xiàn),并不是按屬性的聲明順序來分配的,這樣只占用24 bytes,如果按聲明順序,那應(yīng)該先分配byte, 之后再分配 int 這樣為了對齊,會額外增加 3 個 bytes的(alignment/padding gap)最后可能會占用到32bytes。

為了減少空間浪費,一般情況下,field分配的優(yōu)先依次順序是:

double > long > int > float > char > short > byte > boolean > object reference。

這里有個基本的原則是:盡可能先分配占用空間大的類型。

 

責(zé)任編輯:武曉燕 來源: Tomcat那些事兒
相關(guān)推薦

2013-02-25 14:46:49

2024-11-19 16:14:46

Java開發(fā)

2021-06-08 11:42:12

Pandas數(shù)據(jù)分析Python

2023-01-03 12:30:25

架構(gòu)CPUGPU

2021-06-06 13:10:12

FlinkPvUv

2016-10-19 13:32:31

JavaMemory

2009-06-23 14:23:00

名稱空間Java API

2011-07-08 15:11:03

JAVA

2010-06-03 08:55:43

LINQ

2013-08-21 11:31:21

iPhone圖片方法

2009-09-18 12:29:55

2010-05-17 15:17:06

MySQL常用操作

2010-02-04 16:14:56

Ubuntu QQ

2020-01-10 16:23:44

Springboot停止服務(wù)Java

2020-08-24 08:05:47

JavaScriptJavaScript 頁面

2009-09-09 11:24:46

PHP實現(xiàn)MVC

2021-02-26 13:20:48

Shell空行Linux

2009-08-31 09:19:31

c#隱藏窗口

2011-06-16 10:48:33

session

2016-03-08 09:52:22

xcode插件開發(fā)
點贊
收藏

51CTO技術(shù)棧公眾號