摘要:對于不同的實現(xiàn),對象占用的內(nèi)存空間大小可能不盡相同,本文主要分析中的情況,實驗環(huán)境為位系統(tǒng),使用進(jìn)行結(jié)論驗證。內(nèi)存占用這里分析一個只有一組鍵值對的結(jié)構(gòu)如下首先分析本身的大小。
本文深入分析并驗證了不同Java對象占用內(nèi)存空間大小的情況。對于不同的jvm實現(xiàn),Java對象占用的內(nèi)存空間大小可能不盡相同,本文主要分析HotSpot jvm中的情況,實驗環(huán)境為64位window10系統(tǒng)、JDK1.8,使用JProfiler進(jìn)行結(jié)論驗證。
Java對象內(nèi)存布局Java對象的內(nèi)存布局包括:對象頭(Header),實例數(shù)據(jù)(Instance Data)和補齊填充(Padding)。
對象頭:關(guān)于對象頭的詳細(xì)介紹可以參看我的博文:http://blog.csdn.net/codersha...,這里只關(guān)注其內(nèi)存占用大小。在64位機器上,默認(rèn)不開啟指針壓縮(-XX:-UseCompressedOops)的情況下,對象頭占用12bytes,開啟指針壓縮(-XX:+UseCompressedOops)則占用16bytes。
實例數(shù)據(jù):原生類型(primitive type)的內(nèi)存占用如下:
Primitive Type | Memory Required(bytes) |
---|---|
byte, boolean | 1 byte |
short, char | 2 bytes |
int, float | 4 bytes |
long, double | 8 bytes |
對象引用(reference)類型在64位機器上,關(guān)閉指針壓縮時占用8bytes, 開啟時占用4bytes。
對齊填充:Java對象占用空間是8字節(jié)對齊的,即所有Java對象占用bytes數(shù)必須是8的倍數(shù)。例如,一個包含兩個屬性的對象:int和byte,并不是占用17bytes(12+4+1),而是占用24bytes(對17bytes進(jìn)行8字節(jié)對齊)
Java對象內(nèi)存占用首先根據(jù)以上的計算規(guī)則,進(jìn)行一個簡單的驗證。使用下面的程序進(jìn)行驗證:
public class Test { public static void main(String[] args) throws InterruptedException { TestObject testObject = new TestObject(); Thread.sleep(600 * 1000); System.out.println(testObject); } } class TestObject { private int i; private double d; private char[] c; public TestObject() { this.i = 1; this.d = 1.0; this.c = new char[]{"a", "b", "c"}; } }
TestObject對象有四個屬性,分別為int, double, Byte, char[]類型。在打開指針壓縮(-XX:+UseCompressedOops)的情況下,在64位機器上,TestObject占用的內(nèi)存大小應(yīng)為:
12(Header) + 4(int) + 8(double) + 4(reference) = 28 (bytes),加上8字節(jié)對齊,最終的大小應(yīng)為32 bytes。
JProfiler中的結(jié)果為:
可以看到,TestObject占用的內(nèi)存空間大?。⊿hallow Size)為32 bytes。
關(guān)于Retained Size和Shallow Size的區(qū)別,可以參看:
http://blog.csdn.net/e5945/ar...
當(dāng)指針壓縮關(guān)閉時(-XX:-UseCompressedOops),在64位機器上,TestObject占用的內(nèi)存大小應(yīng)為:
16(Header) + 4(int) + 8(double) + 8(reference) = 36 (bytes), 8字節(jié)對齊后為 40 bytes。
JProfile的結(jié)果為:
包裝類型包裝類(Boolean/Byte/Short/Character/Integer/Long/Double/Float)占用內(nèi)存的大小等于對象頭大小加上底層基礎(chǔ)數(shù)據(jù)類型的大小。
包裝類型的對象內(nèi)存占用情況如下:
Numberic Wrappers | +useCompressedOops | -useCompressedOops |
---|---|---|
Byte, Boolean | 16 bytes | 24 bytes |
Short, Character | 16 bytes | 24 bytes |
Integer, Float | 16 bytes | 24 bytes |
Long, Double | 24 bytes | 24 bytes |
64位機器上,數(shù)組對象的對象頭占用24 bytes,啟用壓縮后占用16字節(jié)。比普通對象占用內(nèi)存多是因為需要額外的空間存儲數(shù)組的長度。基礎(chǔ)數(shù)據(jù)類型數(shù)組占用的空間包括數(shù)組對象頭以及基礎(chǔ)數(shù)據(jù)類型數(shù)據(jù)占用的內(nèi)存空間。由于對象數(shù)組中存放的是對象的引用,所以對象數(shù)組本身的大小=數(shù)組對象頭+length * 引用指針大小,總大小為對象數(shù)組本身大小+存放的數(shù)據(jù)的大小之和。
舉兩個例子:
int[10]:
開啟壓縮:16 + 10 * 4 = 56 bytes;
關(guān)閉壓縮:24 + 10 * 4 = 64bytes。
new Integer[3]:
關(guān)閉壓縮: Integer數(shù)組本身:24(header) + 3 * 8(Integer reference) = 48 bytes; 總共:48 + 3 * 24(Integer) = 120 bytes。
開啟壓縮: Integer數(shù)組本身:16(header) + 3 * 4(Integer reference) = 28(padding) -> 32 (bytes) 總共:32 + 3 * 16(Integer) = 80 (bytes)String
在JDK1.7及以上版本中,String包含2個屬性,一個用于存放字符串?dāng)?shù)據(jù)的char[], 一個int類型的hashcode, 部分源代碼如下:
public final class String implements java.io.Serializable, Comparable, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 }
因此,在關(guān)閉指針壓縮時,一個String本身需要 16(Header) + 8(char[] reference) + 4(int) = 32 bytes。
除此之外,一個char[]占用24 + length * 2 bytes(8字節(jié)對齊), 即一個String占用的內(nèi)存空間大小為:
56 + length * 2 bytes (8字節(jié)對齊)。
舉幾個例子。
一個空字符串("")的大小應(yīng)為:56 + 0 * 2 bytes = 56 bytes。
JProfiler結(jié)果:
字符串"abc"的大小應(yīng)為:56 + 3 * 2 = 62(8字節(jié)對齊)->64 (bytes)
字符串"abcde"的大小應(yīng)為:56 + 5 * 2 = 66->72 (bytes)
字符串"abcde"在開啟指針壓縮時的大小為:
String本身:12(Header) + 4(char[] reference) + 4(int hash) = 20(padding) -> 24 (bytes); 存儲數(shù)據(jù):16(char[] header) + 5*2 = 26(padding) -> 32 (bytes) 總共:24 + 32 = 56 (bytes)常用數(shù)據(jù)結(jié)構(gòu)內(nèi)存占用
根據(jù)上面的內(nèi)存占用計算規(guī)則,可以計算出一個對象在內(nèi)存中的占用空間大小情況,下面舉例分析下Java中的Enum, ArrayList及HashMap的內(nèi)存占用情況,讀者可以仿照分析計算過程來計算其他數(shù)據(jù)結(jié)構(gòu)的內(nèi)存占用情況。
注: 下面的分析計算基于HotSpot Jvm, JDK1.8, 64位機器,開啟指針壓縮。
枚舉類創(chuàng)建enum時,編譯器會生成一個相關(guān)的類,這個類繼承自java.lang.Enum。Enum類擁有兩個屬性變量,分別為int的ordinal和String的name, 相關(guān)源碼如下:
public abstract class Enum> implements Comparable , Serializable { /** * The name of this enum constant, as declared in the enum declaration. * Most programmers should use the {@link #toString} method rather than * accessing this field. */ private final String name; /** * The ordinal of this enumeration constant (its position * in the enum declaration, where the initial constant is assigned * an ordinal of zero). * * Most programmers will have no use for this field. It is designed * for use by sophisticated enum-based data structures, such as * {@link java.util.EnumSet} and {@link java.util.EnumMap}. */ private final int ordinal; }
以下面的TestEnum為例進(jìn)行枚舉類的內(nèi)存占用分析
public enum TestEnum { ONE(1, "one"), TWO(2, "two"); private int code; private String desc; TestEnum(int code, String desc) { this.code = code; this.desc = desc; } public int getCode() { return code; } public String getDesc() { return desc; } }
這里TestEnum的每個實例除了父類的兩個屬性外,還擁有一個int的code及String的desc屬性,所以一個TestEnum的實例本身所占用的內(nèi)存大小為:
12(header) + 4(ordinal) + 4(name reference) + 4(code) + 4(desc reference) = 28(padding) -> 32 bytes.
總共占用的內(nèi)存大小為:
按照上面對字符串類型的分析,desc和name都占用:48 bytes。
所以TestEnum.ONE占用總內(nèi)存大小為:
12(header) + 4(ordinal) + 4(code) + 48 * 2(desc, name) + 4(desc reference) + 4(name reference) = 128 (bytes)
JProfiler中的結(jié)果可以驗證上述分析:
在分析ArrayList的內(nèi)存之前,有必須先了解下ArrayList的實現(xiàn)原理。
ArrayList實現(xiàn)List接口,底層使用數(shù)組保存所有元素。其操作基本上是對數(shù)組的操作。下面分析下源代碼:
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access
ArrayList提供了三種方式的構(gòu)造器,可以構(gòu)造一個默認(rèn)的空列表、構(gòu)造一個指定初始容量的空列表及構(gòu)造一個包含指定collection元素的列表,這些元素按照該collection的迭代器返回它們的順序排列。
/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * Constructs an empty list with the specified initial capacity. * * @ initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection"s * iterator. * * @ c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
ArrayList提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection extends E> c)等,這里著重介紹一下add(E e)方法。
/** * Appends the specified element to the end of this list. * * @ e element to be appended to this list * @return true (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
add方法將指定的元素添加到此列表的尾部。這里注意下ensureCapacityInternal方法,這個方法會檢查添加后元素的個數(shù)是否會超過當(dāng)前數(shù)組的長度,如果超出,數(shù)組將會進(jìn)行擴容。
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10; /** * Increases the capacity of this ArrayList instance, if * necessary, to ensure that it can hold at least the number of elements * specified by the minimum capacity argument. * * @ minCapacity the desired minimum capacity */ public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It"s already // supposed to be at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
如果初始時沒有指定ArrayList大小,在第一次調(diào)用add方法時,會初始化數(shù)組默認(rèn)最小容量為10??聪耮row方法的源碼:
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @ minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
從上述代碼可以看出,數(shù)組進(jìn)行擴容時,會將老數(shù)組中的元素重新拷貝一份到新的數(shù)組中,每次數(shù)組擴容的增長是原容量的1.5倍。這種操作的代價是很高的,因此在實際使用時,應(yīng)該盡量避免數(shù)組容量的擴張。當(dāng)可預(yù)知要保存的元素的數(shù)量時,要在構(gòu)造ArrayList實例時,就指定其容量,以避免數(shù)組擴容的發(fā)生?;蛘吒鶕?jù)實際需求,通過調(diào)用ensureCapacity方法來手動增加ArrayList實例的容量。
ArrayList其他操作讀取刪除等原理這里不作介紹了。
下面開始分析ArrayList的內(nèi)存占用情況。ArrayList繼承AbstractList類,AbstractList擁有一個int類型的modCount屬性,ArrayList本身擁有一個int類型的size屬性和一個數(shù)組屬性。
所以一個ArrayList實例本身的的大小為:
12(header) + 4(modCount) + 4(size) + 4(elementData reference) = 24 (bytes)
下面分析一個只有一個Integer(1)元素的ArrayList
ArrayListtestList = Lists.newArrayList(); testList.add(1);
根據(jù)上面對ArrayList原理的介紹,當(dāng)調(diào)用add方法時,ArrayList會初始化一個默認(rèn)大小為10的數(shù)組,而數(shù)組中保存的Integer(1)實例大小為16 bytes。
則testList占用的內(nèi)存大小為:
24(ArrayList itselft) + 16(elementData array header) + 10 * 4(elemetData reference) + 16(Integer) = 96 (bytes)
JProfiler中的結(jié)果驗證了上述分析:
HashMap要分析HashMap的內(nèi)存占用,同樣需要先了解HashMap的實現(xiàn)原理。
HashMap是一個“鏈表散列”的數(shù)據(jù)結(jié)構(gòu),即數(shù)組和鏈表的結(jié)合體。
從圖上可以看出,HashMap底層是一個數(shù)組結(jié)構(gòu),數(shù)組中的每一項又是一個鏈表。當(dāng)新建一個HashMap的時候,初始化一個數(shù)組,源碼如下:
/** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also tolerate length zero in some operations to allow * bootstrapping mechanics that are currently not needed.) */ transient Node[] table;
Node是鏈表中一個結(jié)點,一個Node對象保存了一對HashMap的Key,Value以及指向下一個節(jié)點的指針,源碼如下:
/** * Basic hash bin node, used for most entries. (See below for * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) */ static class Nodeimplements Map.Entry { final int hash; final K key; V value; Node next; Node(int hash, K key, V value, Node next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } }
HashMap提供了四種方式的構(gòu)造器,分別為指定初始容量及負(fù)載因子構(gòu)造器,指定初始容量構(gòu)造器,不指定初始容量及負(fù)載因子構(gòu)造器,以及根據(jù)已有Map生成新Map的構(gòu)造器。
/** * Constructs an empty HashMap with the specified initial * capacity and load factor. * * @ initialCapacity the initial capacity * @ loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } /** * Constructs an empty HashMap with the specified initial * capacity and the default load factor (0.75). * * @ initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Constructs an empty HashMap with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } /** * Constructs a new HashMap with the same mappings as the * specified Map. The HashMap is created with * default load factor (0.75) and an initial capacity sufficient to * hold the mappings in the specified Map. * * @ m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ public HashMap(Map extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); }
如果不指定初始容量及負(fù)載因子,默認(rèn)的初始容量為16, 負(fù)載因子為0.75。
負(fù)載因子衡量的是一個散列表的空間的使用程度,負(fù)載因子越大表示散列表的裝填程度越高,反之愈小。對于使用鏈表法的散列表來說,查找一個元素的平均時間是O(1+a),因此如果負(fù)載因子越大,對空間的利用更充分,然而后果是查找效率的降低;如果負(fù)載因子太小,那么散列表的數(shù)據(jù)將過于稀疏,對空間造成嚴(yán)重浪費。
HashMap有一個容量閾值屬性threshold,是根據(jù)初始容量和負(fù)載因子計算得出threshold=capacity*loadfactor, 如果HashMap中數(shù)組元素的個數(shù)超過這個閾值,則HashMap會進(jìn)行擴容。HashMap底層的數(shù)組長度總是2的n次方,每次擴容容量為原來的2倍。
擴容的目的是為了減少hash沖突,提高查詢效率。而在HashMap數(shù)組擴容之后,最消耗性能的點就出現(xiàn)了:原數(shù)組中的數(shù)據(jù)必須重新計算其在新數(shù)組中的位置,并放進(jìn)去,這就是resize。
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * Implements Map.put and related methods * * @ hash hash for key * @ key the key * @ value the value to put * @ onlyIfAbsent if true, don"t change existing value * @ evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node[] tab; Node p; int n, i; //初始化數(shù)組的大小為16,容量閾值為16*0.75=12 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //如果key的hash值對應(yīng)的數(shù)組位置沒有元素,則新建Node放入此位置 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode )p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
從上面的源代碼中可以看出:當(dāng)我們往HashMap中put元素的時候,先根據(jù)key的hashCode重新計算hash值,根據(jù)hash值得到這個元素在數(shù)組中的位置(即下標(biāo)),如果數(shù)組該位置上已經(jīng)存放有其他元素了,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數(shù)組該位置上沒有元素,就直接將該元素放到此數(shù)組中的該位置上。
關(guān)于HashMap數(shù)據(jù)的讀取這里就不作介紹了。
這里分析一個只有一組鍵值對的HashMap, 結(jié)構(gòu)如下:
MaptestMap = Maps.newHashMap(); testMap.put(1, 2);
首先分析HashMap本身的大小。HashMap對象擁有的屬性包括:
/** * The table, initialized on first use, and resized as * necessary. When allocated, length is always a power of two. * (We also tolerate length zero in some operations to allow * bootstrapping mechanics that are currently not needed.) */ transient Node[] table; /** * Holds cached entrySet(). Note that AbstractMap fields are used * for keySet() and values(). */ transient Set > entrySet; /** * The number of key-value mappings contained in this map. */ transient int size; /** * The number of times this HashMap has been structurally modified * Structural modifications are those that change the number of mappings in * the HashMap or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the HashMap fail-fast. (See ConcurrentModificationException). */ transient int modCount; /** * The next size value at which to resize (capacity * load factor). * * @serial */ // (The javadoc description is true upon serialization. // Additionally, if the table array has not been allocated, this // field holds the initial array capacity, or zero signifying // DEFAULT_INITIAL_CAPACITY.) int threshold; /** * The load factor for the hash table. * * @serial */ final float loadFactor;
HashMap繼承了AbstractMap
transient SetkeySet; transient Collection values;
所以一個HashMap對象本身的大小為:
12(header) + 4(table reference) + 4(entrySet reference) + 4(size) + 4(modCount) + 4(threshold) + 8(loadFactor) + 4(keySet reference) + 4(values reference) = 48(bytes)
接著分析testMap實例在總共占用的內(nèi)存大小。
根據(jù)上面對HashMap原理的介紹,可知每對鍵值對對應(yīng)一個Node對象。根據(jù)上面的Node的數(shù)據(jù)結(jié)構(gòu),一個Node對象的大小為:
12(header) + 4(hash reference) + 4(key reference) + 4(value reference) + 4(next pointer reference) = 28 (padding) -> 32(bytes)
加上Key和Value兩個Integer對象,一個Node占用內(nèi)存總大小為:32 + 2 * 16 = 64(bytes)
JProfiler中結(jié)果:
下面分析HashMap的Node數(shù)組的大小。
根據(jù)上面HashMap的原理可知,在不指定容量大小的情況下,HashMap初始容量為16,所以testMap的Node[]占用的內(nèi)存大小為:
16(header) + 16 * 4(Node reference) + 64(Node) = 144(bytes)
JProfile結(jié)果:
所以,testMap占用的內(nèi)存總大小為:
48(map itself) + 144(Node[]) = 192(bytes)
JProfile結(jié)果:
這里只用一個例子說明如何對HashMap進(jìn)行占用內(nèi)存大小的計算,根據(jù)HashMap初始化容量的大小,以及擴容的影響,HashMap占用內(nèi)存大小要進(jìn)行具體分析,
不過思路都是一致的。
以上對計算Java對象占用內(nèi)存的基本規(guī)則及方法進(jìn)行了介紹,并通過分析枚舉類,ArrayList, HashMap的內(nèi)存占用情況介紹了分析復(fù)雜對象內(nèi)存占用的基本方法,
實際工作中需要精確計算Java對象內(nèi)存占用情況的場景不多,不過作為Java程序員的基本素養(yǎng),了解這方面內(nèi)容還是很有必要的。
An overview of memory saving techniques in Java
Memory consumption of popular Java data types – part 1
Memory consumption of popular Java data types – part 2
memory usage compare hashmap, arraylist, and array
How does a HashMap work in JAVA
一個Java對象到底占用多大內(nèi)存?
深入Java集合學(xué)習(xí)系列:HashMap的實現(xiàn)原理
深入Java集合學(xué)習(xí)系列:ArrayList的實現(xiàn)原理
《Java編程思想》
《深入理解Java虛擬機》
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65167.html
摘要:堆內(nèi)存使用分析,垃圾收集器日志解讀重要的東東在中,對象實例都是在堆上創(chuàng)建。機制是由提供,用來清理需要清除的對象,回收堆內(nèi)存。在中,是由一個被稱為垃圾回收器的守護(hù)線程執(zhí)行的。 堆內(nèi)存使用分析,垃圾收集器 GC 日志解讀 重要的東東 在Java中,對象實例都是在堆上創(chuàng)建。一些類信息,常量,靜態(tài)變量等存儲在方法區(qū)。堆和方法區(qū)都是線程共享的。 GC機制是由JVM提供,用來清理需要清除的對象,...
摘要:小心遞歸中內(nèi)存泄漏前段時間由于業(yè)務(wù)需要,需要從數(shù)據(jù)庫中查詢出來所有滿足條件的數(shù)據(jù),然后導(dǎo)入到文件中。綜上,我們可以得知程序出現(xiàn)了內(nèi)存泄漏。 小心遞歸中內(nèi)存泄漏 前段時間由于業(yè)務(wù)需要,需要從數(shù)據(jù)庫中查詢出來所有滿足條件的數(shù)據(jù),然后導(dǎo)入到文件中。于是隨便寫了個程序,查詢出所有滿足條件然后再寫入文件。但是實際上線后卻發(fā)現(xiàn),程序剛開始運行馬上看到部分?jǐn)?shù)據(jù)寫入到文件,但是后面運行越來越慢,于是對...
摘要:分析應(yīng)用靜息態(tài)內(nèi)存占用。這里采用的方式是靜息態(tài)內(nèi)存進(jìn)入,立即內(nèi)存操作一段時間之后再內(nèi)存一共有三次,可以利用對比的功能對比內(nèi)存增量。 作者:楊超,騰訊移動客戶端開發(fā) 工程師 商業(yè)轉(zhuǎn)載請聯(lián)系騰訊WeTest獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。原文鏈接:http://wetest.qq.com/lab/view/359.html WeTest 導(dǎo)讀 歷時五天的內(nèi)存優(yōu)化已經(jīng)結(jié)束,這里總結(jié)一下這幾天...
摘要:內(nèi)存溢出分配的內(nèi)存空間超過系統(tǒng)內(nèi)存。內(nèi)存泄漏的原因分析由大塊組成堆,棧,本地方法棧,程序計數(shù)器,方法區(qū)。內(nèi)存溢出的原因分析內(nèi)存溢出是由于沒被引用的對象垃圾過多造成沒有及時回收,造成的內(nèi)存溢出。小結(jié)棧內(nèi)存溢出程序所要求的棧深度過大導(dǎo)致。 showImg(https://segmentfault.com/img/bVbweuq?w=563&h=300); 前言:JVM中除了程序計數(shù)器,其他...
閱讀 1388·2021-10-08 10:04
閱讀 2713·2021-09-22 15:23
閱讀 2735·2021-09-04 16:40
閱讀 1187·2019-08-29 17:29
閱讀 1508·2019-08-29 17:28
閱讀 3004·2019-08-29 14:02
閱讀 2235·2019-08-29 13:18
閱讀 860·2019-08-23 18:35