java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2858)
at java.util.BitSet.ensureCapacity(BitSet.java:155)
at java.util.BitSet.expandTo(BitSet.java:170)
at java.util.BitSet.set(BitSet.java:265)
项目中大量使用了BitSet,在一次小需求中,我将原始的数据到处到临时数据库,并且做了一些初始化,项目run起来的会后要去加载库中所有数据到内存。
其中有BitSet bitSet=new BitSet();bitSet.set(X),这么一行代码,这个X是从数据库加载的,表中大概有几千条数据,一加载就内存溢出,后来终于分析到是因为我在初始化DB的时候将X都赋值为该条记录id,因为是开发库,id已经是亿以上的数字了,导致X都是几亿几亿的,而原来是1千以下,再来.set(X),就很耗用内存了,用visulaVM工具分析发现long型数组占用了45%的堆内存,仔细了解一下BitSet数据存储方式就明白了.
它是大小可动态改变, 取值为true或false的位集合。用于表示一组布尔标志。
默认占64个bit。不够用时会自动扩展64的整数倍.
补充说明一下,我当时的场景大概的原理如下:
/**
* BitSet导致使用不当导致堆内存溢出
* @author bingyin.gby
* @version $Id: Test.java, v 0.1 2014年5月23日 下午1:52:27 bingyin.gby Exp $
*/
public class Test {
private static List<BitSet> bitSetList = new ArrayList<BitSet>();
/**
* 问题原因:因为数据库中的key_index设置过大导致内存泄露,
* 我系统中得到的BitSet是要保留在内存中,以便下一次直接使用。
* 因为key_index最初我们库中都是比较小的,所以无问题,在一个
* 开发库中把所有key_index值更新为它自己的id,而恰好这个库中的id是比较大的,就引发了这个问题
* @param args
*/
public static void main(String[] args) {
long totalSize = 0;
for (int key_index = 300000; key_index < 305000; key_index++) {
BitSet bitSet = new BitSet();
try {
bitSet.set(key_index);
bitSetList.add(bitSet);
totalSize += bitSet.size() / 8 / 1024;
System.out.println("累计:" + totalSize + "kb");
} catch (Throwable e) {
System.out.println(key_index + "累计:" + totalSize / 1024 + "mb导致了内存溢出");
System.exit(1);
}
}
}
}
补充说明:
定义
一个大小可动态改变, 取值为true或false的位集合。用于表示一组布尔标志。
位的值为布尔型,初始大小为64bit,初始值均为“false”
0000000000000000000000000000000000000000000000000000000000000000 共64位。
如果要存储数字5,则需要:00005……. 0共64位
如果要存储数字100,则需要:00000…….100….0共128位
常用方法
void set(int bitIndex):将bitIndex位置的值设为“true”或者给定的值
void or(BitSet other): other同该字位集进行或操作,结果作为该字位集的新值。
void and(BitSet other): other同该字位集进行与操作,结果作为该字位集的新值。
boolean equals(BitSet bs):比较两个BitSet是否相等。