概述
就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> </span></li><li class=""><span><span class="comment"> * VM args : -Xms20M -XX:MaxDirectMemorySize=10M</span> </span></li><li class="alt"><span><span class="comment"> * </span> </span></li><li class=""><span><span class="comment"> */</span><span> </span></span></li><li class="alt"><span><span class="keyword">public</span><span> </span><span class="keyword">class</span><span> DirectMemoryOOM { </span></span></li><li class=""><span> <span class="keyword">private</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">final</span><span> </span><span class="keyword">int</span><span> _1MB = </span><span class="number">1024</span><span> * </span><span class="number">1024</span><span>; </span></span></li><li class="alt"><span> </span></li><li class=""><span> <span class="keyword">public</span><span> </span><span class="keyword">static</span><span> </span><span class="keyword">void</span><span> main(String[] args) </span><span class="keyword">throws</span><span> IllegalArgumentException, IllegalAccessException { </span></span></li><li class="alt"><span> Field unsafeField=Unsafe.<span class="keyword">class</span><span>.getDeclaredFields()[</span><span class="number">0</span><span>]; </span></span></li><li class=""><span> unsafeField.setAccessible(<span class="keyword">true</span><span>); </span></span></li><li class="alt"><span> Unsafe unsafe=(Unsafe) unsafeField.get(<span class="keyword">null</span><span>); </span></span></li><li class=""><span> <span class="keyword">while</span><span>(</span><span class="keyword">true</span><span>){ </span></span></li><li class="alt"><span> unsafe.allocateMemory(_1MB); </span></li><li class=""><span> } </span></li><li class="alt"><span> } </span></li><li class=""><span>} </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设定的值,服务器物理内存几乎被耗尽。
存在的问题:
- Cache类是单例,在同一个JVM里,不能根据实际应用创建不同的Cache(可以实现自己的Cache);
- OffHeapMemoryBuffer中ByteBuffer 空间存在浪费;
- 要根据业务类型,合理分配OffHeapMemoryBuffer的容量;
- 目前Map<key>还是存放在Heap里,只是Value存放在Off-Heap,不过可以根据需要修改代码;
Direct-Memory依赖的其他类库
- JoSQL SQL for Java Objects http://josql.sourceforge.net/
- guava google-common-collection http://code.google.com/p/guava-libraries/
- 其中的MapMaker的使用
参考:
- JDK1.4引入的DirectByteBuffer
- BigMemory
最后
以上就是怕孤独手链为你收集整理的DirectMemory(堆外(Off-Heap)缓存BigMemory的一个实现)的全部内容,希望文章能够帮你解决DirectMemory(堆外(Off-Heap)缓存BigMemory的一个实现)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复