DirectMemory(堆外(Off-Heap)缓存BigMemory的一个实现)

 就Java 本地Cache(非分布式)而言,存在3个地方可以存放数据:Heap, OffHeap和Disk



BigMemory非开源产品,所以其源码不能通过正常渠道拿到。同时,有一个开源产品DirectMemory实现了类似BigMemory的Cache实现

这是github的地址:https://github.com/raffaeleguidi/DirectMemory


DirectMemory是堆外(Off-Heap)缓存BigMemory的一个实现。它能够在内存中序列化大批量Java对象,而不影响JVM垃圾收集的性能。

DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与JAVA堆的最大值(-Xmx指定)一样。


它的类图:


Cache.init(10, 100);
Cache.put("test1", "t".getBytes());
Cache.put("test2", "t".getBytes());
Cache.put("test3", "t".getBytes());

byte[] p = Cache.retrieve("test1");

以下代码越过了DirectByteBuffer类,直接通过反射获取Unsafe实例并进行内存分配(Unsafe类的getUnsafe()方法限制类只有引导类加载器才会返回实例,也就是设计者希望只有rt.jar中的类才能使用Unsafe的功能)。因为,虽然使用DirectByteBuffer分配内存也会抛出内存溢出异常,但它抛出异常时并没有真正向操作系统申请分配内存,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请分配内存的方式是unsafe.allocateMemory().


<li class="alt"><span><span class="comment">/**</span>&nbsp;</span></li><li class=""><span><span class="comment">&nbsp;*&nbsp;VM&nbsp;args&nbsp;:&nbsp;-Xms20M&nbsp;-XX:MaxDirectMemorySize=10M</span>&nbsp;</span></li><li class="alt"><span><span class="comment">&nbsp;*&nbsp;</span>&nbsp;</span></li><li class=""><span><span class="comment">&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;DirectMemoryOOM&nbsp;{&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;_1MB&nbsp;=&nbsp;</span><span class="number">1024</span><span>&nbsp;*&nbsp;</span><span class="number">1024</span><span>;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;</span><span class="keyword">throws</span><span>&nbsp;IllegalArgumentException,&nbsp;IllegalAccessException&nbsp;{&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Field&nbsp;unsafeField=Unsafe.<span class="keyword">class</span><span>.getDeclaredFields()[</span><span class="number">0</span><span>];&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsafeField.setAccessible(<span class="keyword">true</span><span>);&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Unsafe&nbsp;unsafe=(Unsafe)&nbsp;unsafeField.get(<span class="keyword">null</span><span>);&nbsp;&nbsp;</span></span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">while</span><span>(</span><span class="keyword">true</span><span>){&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsafe.allocateMemory(_1MB);&nbsp;&nbsp;</span></li><li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li><li class=""><span>}&nbsp;&nbsp;</span></li>


运行结果 :Exception in thread "main" java.lang.OutOfMemoryError


需要注意的问题

DirectMemory是java nio引入的,直接以native的方式分配内存,不受jvm管理。这种方式是为了提高网络和文件IO的效率,避免多余的内存拷贝而出现的。 DirectMemory占用的大小没有直接的工具或者API可以查看,不过这个在Bits类中是有两个字段存储了最大大小和已分配大小的,使用反射可以 拿到这个数据:

Class<?> c = Class.forName(“java.nio.Bits”);
Field maxMemory = c.getDeclaredField(“maxMemory”);
maxMemory.setAccessible(true);
Field reservedMemory = c.getDeclaredField(“reservedMemory”);
reservedMemory.setAccessible(true);
Long maxMemoryValue = (Long)maxMemory.get(null);
Long reservedMemoryValue = (Long)reservedMemory.get(null);

DirectMemory 的默认大小是64M,而JDK6之前和JDK6的某些版本的SUN JVM,存在一个BUG,在用-Xmx设定堆空间大小的时候,也设置了DirectMemory的大小。加入设置了-Xmx2048m,那么jvm最终可 分配的内存大小为4G多一些,是预期的两倍。

所以要特别注意的是,设置jvm参数-XX:MaxDirectMemorySize=128m,指定DirectMemory的大小。否则会造成服务器的JVM占用内存总是持续增长,大大超过-Xmx设定的值,服务器物理内存几乎被耗尽。



存在的问题:

  1. Cache类是单例,在同一个JVM里,不能根据实际应用创建不同的Cache(可以实现自己的Cache);
  2. OffHeapMemoryBuffer中ByteBuffer 空间存在浪费;
  3. 要根据业务类型,合理分配OffHeapMemoryBuffer的容量;
  4. 目前Map<key>还是存放在Heap里,只是Value存放在Off-Heap,不过可以根据需要修改代码;

Direct-Memory依赖的其他类库

  1. JoSQL SQL for Java Objects http://josql.sourceforge.net/
  2. guava google-common-collection http://code.google.com/p/guava-libraries/
  3. 其中的MapMaker的使用

  参考:

  1. JDK1.4引入的DirectByteBuffer
  2. BigMemory


阅读更多
换一批

没有更多推荐了,返回首页