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

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

DirectMemory(堆外(Off-Heap)缓存BigMemory的一个实现)_第1张图片


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

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


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

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


它的类图:

DirectMemory(堆外(Off-Heap)缓存BigMemory的一个实现)_第2张图片

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().


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


  • 运行结果 :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还是存放在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


    你可能感兴趣的:(java基础)