數(shù)據(jù)庫數(shù)據(jù)在
Java占用內(nèi)存簡單估算
結(jié)論:
1.數(shù)據(jù)庫記錄放在JAVA里,用對象(ORM一般的處理方式)需要4倍左右的內(nèi)存空間,用HashMap這種KV保存需要10倍空間;
2.如果你主要數(shù)據(jù)是text大文本,那空間一般可以按2倍估算。
以上是一個通用數(shù)據(jù)
測試結(jié)論,估大家參考。
數(shù)據(jù)庫記錄占用的空間大小比較好算,比如一個int占用4字節(jié),bigint占用8字節(jié),date占用3字節(jié),datetime占用8字節(jié),varchar是變長字節(jié)等。如果不想精確計算,在數(shù)據(jù)庫中通過統(tǒng)計信息也可以比較輕松的知道表總共占用的空間及每條記錄平均行長。
當我們用JDBC訪問數(shù)據(jù)庫時,經(jīng)常會被問到內(nèi)存溢出的問題,由于java是面向?qū)ο蟮恼Z言,用JVM來自動內(nèi)存回收,不能按普通方法計算內(nèi)存,本文給出一個估算內(nèi)存的思路和參考答案
先給出普通JDBC中數(shù)據(jù)庫對象與內(nèi)存的映射關(guān)系
MySQLOracleJDBC
Int
Integer
Int unsigned
Long
BigInt
Long
BigInt unsigned
BigInteger
Decimal
Number
BigDecimal
Varchar
Varchar2
String
Date
Date
Datetime
Date
Timestamp
Timestamp
Timestamp
Timestamp
Clob
Clob
String
Blob
blob
Byte[]
Text
Clob
String
float
binary_float
float
double
binary_double
double
上面這個比較好理解,接下來我們需要JAVA常用對象的內(nèi)存占用空間,這個可以通過JDK 5 開始提供的Instrumentation 接口來完成,也可以通過開源的
sizeOf.jar 來測試,筆者是通過sizeOf.jar驗證的。測試結(jié)果數(shù)據(jù)如下:
對象
64位 JVM 壓縮指針
64位 JVM 非壓縮指針
Integer
16
24
Long
24
24
Object
16
16
Date
24
32
Timestamp
32
40
String_0
48
64
String_1
56
72
String_10
72
88
String_100
248
264
StringBuilder
24
32
BigDecimal
40
48
BigInteger
64
80
HashMap
128
216
HashMap_0
72
96
HashMap_100
576
1112
HashMap_10000
65600
131160
ArrayList
80
144
ArrayList_0
40
64
ArrayList_100
440
864
ArrayList_10000
40040
80064
LinkedList
48
80
LinkedHashMap
96
144
ClassA
32
40
ClassB
40
48
ClassC
40
56
由于現(xiàn)在主機一般都是64位, 64位JVM從JDK1.6.45開始,當JVM最大內(nèi)存小于32GB時,自動打開壓縮指針特性,這樣對象的內(nèi)存占用空間少很多,由上表可以看出,至少減少1/3的空間。
下面我們結(jié)合數(shù)據(jù)庫數(shù)據(jù)來測試
假如mysql數(shù)據(jù)庫有一張emp表,結(jié)構(gòu)如下:
[sql]
CREATE TABLE `emp` (
`id` int(11) NOT NULL,
`create_time` datetime DEFAULT NULL,
`modify_time` datetime DEFAULT NULL,
`name` varchar(16) DEFAULT NULL,
`address` varchar(256) DEFAULT NULL,
`age` smallint(6) DEFAULT NULL,
`height` decimal(10,2) DEFAULT NULL,
`weight` decimal(10,2) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
樣本數(shù)據(jù)如下:
[java]
hm.put("id", 1988);
hm.put("createTime", new Date());
hm.put("modifyTime", new Date());
hm.put("name", "張三豐");
hm.put("address","浙江杭州市西湖大道188號808室");
hm.put("age",88);
hm.put("weight",new BigDecimal(88));
hm.put("height",new BigDecimal(188));
hm.put("phone","1388888888");
按上面樣本數(shù)據(jù)計算,有效數(shù)據(jù)約80字節(jié),在MySQL里占用空間約120字節(jié)
在java里轉(zhuǎn)換為HashMap和Emp對象測試空間如下
對象
64位 JVM 壓縮指針
64位 JVM 非壓縮指針
HashMap_empty
128
216
HashMap_full
1360
1832
Emp_empty
72
112
Emp_full
464
600
從上面測試結(jié)果看,數(shù)據(jù)到JAVA里占用的空間增加了許多,在64位壓縮指針下,如果存到HashMap,需要1360字節(jié),空間是數(shù)據(jù)庫約11.3倍,如果存為Emp普通對象,需要464字節(jié),是數(shù)據(jù)庫的3.8倍。
如果我們是一個分頁從數(shù)據(jù)庫讀取emp信息,每頁顯示50條記錄,用List保存,HashMap需要68KB,emp對象需要23KB。
根據(jù)這個簡單測試,我們可以總結(jié)一個結(jié)論:
數(shù)據(jù)庫記錄放在JAVA里,用對象(ORM一般的處理方式)需要4倍左右的內(nèi)存空間,用HashMap這種KV保存需要10倍空間。
如果你的數(shù)據(jù)和參考數(shù)據(jù)差異非常大,如主要數(shù)據(jù)text大文本,那空間一般可以簡單的按2倍估算。
以上是一個通用數(shù)據(jù)測試結(jié)論,估大家參考。
下面是測試代碼:
[java]
import net.sourceforge.sizeof.SizeOf;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;
public class TestSize {
static {
SizeOf.skipStaticField(true); //java.sizeOf will not compute static fields
//SizeOf.skipFinalField(true); //java.sizeOf will not compute final fields
//SizeOf.skipFlyweightObject(true); //java.sizeOf will not compute well-known flyweight objects
}
public static void main(String[] args) throws SQLException, IOException, IllegalAccessException {
TestSize ts=new TestSize();
ts.testObjectSize();
ts.testDataSize();
System.out.println("ok");
}
public void testObjectSize() {
System.out.println("Integer:"+SizeOf.deepSizeOf(new Integer(56)));
System.out.println("Long:"+SizeOf.sizeOf(new Long(56L)));
System.out.println("Object:"+SizeOf.sizeOf(new Object()));
System.out.println("Date:"+SizeOf.sizeOf(new Date()));
System.out.println("Timestamp:"+SizeOf.sizeOf(new Timestamp(System.currentTimeMillis())));
System.out.println("String_0:"+SizeOf.deepSizeOf(new String()));
System.out.println("String_1:"+SizeOf.deepSizeOf(new String("1")));
System.out.println("String_10:"+SizeOf.deepSizeOf(new String("0123456789")));
System.out.println("String_100:"+SizeOf.deepSizeOf("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"));
System.out.println("StringBuilder:"+SizeOf.deepSizeOf(new StringBuilder()));
System.out.println("BigDecimal:"+SizeOf.deepSizeOf(new BigDecimal("34535643.23")));
System.out.println("BigInteger:"+SizeOf.deepSizeOf(new BigInteger("34535643")));
System.out.println("HashMap:"+SizeOf.deepSizeOf(new HashMap()));
System.out.println("HashMap_0:"+SizeOf.deepSizeOf(new HashMap(0)));
System.out.println("HashMap_100:"+SizeOf.deepSizeOf(new HashMap(100)));
System.out.println("HashMap_10000:" + SizeOf.deepSizeOf(new HashMap(10000)));
System.out.println("ArrayList:"+SizeOf.deepSizeOf(new ArrayList()));
System.out.println("ArrayList_0:"+SizeOf.deepSizeOf(new ArrayList(0)));
System.out.println("ArrayList_100:"+SizeOf.deepSizeOf(new ArrayList(100)));
System.out.println("ArrayList_10000:"+SizeOf.deepSizeOf(new ArrayList(10000)));
System.out.println("LinkedList:"+SizeOf.deepSizeOf(new LinkedList<Object>()));
System.out.println("LinkedHashMap:"+SizeOf.deepSizeOf(new LinkedHashMap<Object,Object>()));
System.out.println("ClassA:" + SizeOf.deepSizeOf(new ClassA()));
System.out.println("ClassB:"+SizeOf.deepSizeOf(new ClassB()));
System.out.println("ClassC:"+SizeOf.deepSizeOf(new ClassC()));
}
public void testDataSize() throws IOException, IllegalAccessException {
HashMap hm=new HashMap();
System.out.println("HashMap_empty:"+SizeOf.deepSizeOf(hm));
hm.put("id", 1988);
hm.put("createTime", new Date());
hm.put("modifyTime", new Date());
hm.put("name", "張三豐");
hm.put("address","浙江杭州市西湖大道188號808室");
hm.put("age",88);
hm.put("weight",new BigDecimal(88));
hm.put("height",new BigDecimal(188));
hm.put("phone","1388888888");
System.out.println("HashMap_full:" + SizeOf.deepSizeOf(hm));
Emp emp=new Emp();
System.out.println("Emp_empty:"+SizeOf.deepSizeOf(emp));
emp.setId(1988);
emp.setCreateTime(new Timestamp(System.currentTimeMillis()));
emp.setModifyTime(new Timestamp(System.currentTimeMillis()));
emp.setName("張三豐");
emp.setAddress("浙江杭州市西湖大道188號808室");
emp.setAge(28);
emp.setWeight(new BigDecimal("88"));
emp.setHeight(new BigDecimal("188"));
emp.setPhone("13888888888");
System.out.println("Emp_full:"+SizeOf.deepSizeOf(emp));
}
class ClassA{
}
class ClassB extends ClassA{
}
class ClassC extends ClassB{
}
class Emp{
private Integer id;
private Timestamp createTime;
private Timestamp modifyTime;
private String name;
private String address;
private Integer age;
private BigDecimal height;
private BigDecimal weight;
private String phone;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Timestamp getCreateTime() {
return createTime;
}
public void setCreateTime(Timestamp createTime) {
this.createTime = createTime;
}
public Timestamp getModifyTime() {
return modifyTime;
}
public void setModifyTime(Timestamp modifyTime) {
this.modifyTime = modifyTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public BigDecimal getHeight() {
return height;
}
public void setHeight(BigDecimal height) {
this.height = height;
}
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
this.weight = weight;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
}