数据库数据在Java占用内存简单估算
结论:
1.数据库记录放在JAVA里,用对象(ORM一般的处理方式)须要4倍左右的内存空间。用HashMap这样的KV保存须要10倍空间;
2.假设你主要数据是text大文本,那空间一般能够按2倍估算。
以上是一个通用数据測试结论。估大家參考。
数据库记录占用的空间大小比較好算,比方一个int占用4字节。bigint占用8字节。date占用3字节,datetime占用8字节,varchar是变长字节等。假设不想精确计算,在数据库中通过统计信息也能够比較轻松的知道表总共占用的空间及每条记录平均行长。
当我们用JDBC訪问数据库时。常常会被问到内存溢出的问题。因为java是面向对象的语言。用JVM来自己主动内存回收。不能按普通方法计算内存,本文给出一个估算内存的思路和參考答案
先给出普通JDBC中数据库对象与内存的映射关系
MySQL | Oracle | JDBC |
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经常使用对象的内存占用空间,这个能够通过JDK 5 開始提供的Instrumentation 接口来完毕,也能够通过开源的sizeOf.jar 来測试。笔者是通过sizeOf.jar验证的。測试结果数据例如以下:
对象 | 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 |
因为如今主机一般都是64位, 64位JVM从JDK1.6.45開始,当JVM最大内存小于32GB时,自己主动打开压缩指针特性。这样对象的内存占用空间少非常多,由上表能够看出。至少降低1/3的空间。
以下我们结合数据库数据来測试
假如mysql数据库有一张emp表,结构例如以下:
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
样本数据例如以下:
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");
按上面样本数据计算,有效数据约80字节。在MySQL里占用空间约120字节
在java里转换为HashMap和Emp对象測试空间例如以下
对象 | 64位 JVM 压缩指针 | 64位 JVM 非压缩指针 |
HashMap_empty | 128 | 216 |
HashMap_full | 1360 | 1832 |
Emp_empty | 72 | 112 |
Emp_full | 464 | 600 |
从上面測试结果看。数据到JAVA里占用的空间添加了很多,在64位压缩指针下,假设存到HashMap,须要1360字节,空间是数据库约11.3倍,假设存为Emp普通对象,须要464字节,是数据库的3.8倍。
假设我们是一个分页从数据库读取emp信息,每页显示50条记录,用List保存。HashMap须要68KB。emp对象须要23KB。
依据这个简单測试,我们能够总结一个结论:
数据库记录放在JAVA里,用对象(ORM一般的处理方式)须要4倍左右的内存空间。用HashMap这样的KV保存须要10倍空间。
假设你的数据和參考数据差异很大,如主要数据text大文本,那空间一般能够简单的按2倍估算。
以上是一个通用数据測试结论。估大家參考。
以下是測试代码:
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;
}
}
}