Caffeine - 内存消耗

参考自:https://github.com/ben-manes/caffeine/wiki/Memory-overhead

使用 Java Agent for Memory Measurements 来估算运行时的大小。这个大小呢,可能会受到压缩引用,对象填充等的影响。

这个基准测试可能使用 gradle memoryOverhead (执行MemoryBenchmark的)代码在(https://github.com/ben-manes/caffeine/blob/master/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/MemoryBenchmark.java)来运行。


参考自:https://github.com/jbellis/jamm

Jamm提供MemoryMeter,一个java代理,用于测量实际的对象内存使用,包括JVM占用。

如何使用? 在jvm参数中加入 "-javaagent:<path to>/jamm.jar",也就是maven repository中的对应的jamm的jar包位置。

MemoryMeter meter = new MemoryMeter();

long byteSize = meter.measure(object); // 参数对象的内存使用的字节数

long byteSize1 = meter.measureDeep(object); // 参数对象以及引用对象的内存使用的字节数

long numChildren = meter.countChildren(object); // 由参数对象引用的子对象的数量

=========================================================================================

MemoryMeter meter = new MemoryMeter().enableDebug(); // 树形输出

以Caffeine.newBuilder().build() 对象为例。输出如下:

root [com.github.benmanes.caffeine.cache.UnboundedLocalCache$UnboundedLocalManualCache] 704 bytes (24 bytes)
  |
  +--cache [com.github.benmanes.caffeine.cache.UnboundedLocalCache] 680 bytes (56 bytes)
    |
    +--data [java.util.concurrent.ConcurrentHashMap] 64 bytes (64 bytes)
    |
    +--statsCounter [com.github.benmanes.caffeine.cache.stats.DisabledStatsCounter] 24 bytes (24 bytes)
    |
    +--writer [com.github.benmanes.caffeine.cache.DisabledWriter] 24 bytes (24 bytes)
    |
    +--executor [java.util.concurrent.ForkJoinPool] 432 bytes (312 bytes)
    |  |
    |  +--factory [java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory] 16 bytes (16 bytes)
    |  |
    |  +--workerNamePrefix [java.lang.String] 104 bytes (24 bytes)
    |    |
    |    +--value [char[]] 80 bytes (80 bytes)
    |
    +--ticker [com.github.benmanes.caffeine.cache.DisabledTicker] 80 bytes (24 bytes)
      |
      +--name [java.lang.String] 56 bytes (24 bytes)
        |
        +--value [char[]] 32 bytes (32 bytes)

root [com.github.benmanes.caffeine.cache.UnboundedLocalCache$UnboundedLocalManualCache] 
  |
  +--cache [com.github.benmanes.caffeine.cache.UnboundedLocalCache] 
    |
    +--data [java.util.concurrent.ConcurrentHashMap] 
    |
    +--statsCounter [com.github.benmanes.caffeine.cache.stats.DisabledStatsCounter] 
    |
    +--writer [com.github.benmanes.caffeine.cache.DisabledWriter] 
    |
    +--executor [java.util.concurrent.ForkJoinPool] 
    |  |
    |  +--factory [java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory] 
    |  |
    |  +--workerNamePrefix [java.lang.String] 
    |    |
    |    +--value [char[]] 
    |
    +--ticker [com.github.benmanes.caffeine.cache.DisabledTicker] 
      |
      +--name [java.lang.String] 
        |
        +--value [char[]] 

=========================================================================================

MemoryMeter和java.lang.instrument.Instrumentation.getObjectSize一样准确。

使用反射来爬取对象用于measureDeep的对象表。反射是缓慢的,测量过Cassandra Memtable对于100万的对象,花费了5秒。

默认情况下,MemoryMeter使用带有一个IdentityHashMap的measureDeep来跟踪子类。这防止了超出计算和无限循环。当然了,跟踪增加了内存的开销。可以在MemoryMeter构造器中传递一个不同的tracker provider。Jamm提供了AlwaysEmptySet, 这个允许调用add()但是从来不会记得任何东西。一个更有用的选项就是使用一个Bloom filter来实现一个probabilistic set 接口,但是超过了Jamm的范围。

=========================================================================================

回到Caffeine的介绍中,通过对比java5的ConcurrentHashMap,Guava有一个小的优势。尤其是在使用弱引用和弱引用的缓存会很明显。Caffeien通过包装java8的ConcurrentHashMap,使用额外的属性来删除一个被回收的键值对。

Caffeine可能延迟初始化或者在使用中,动态的重置它的内部数据结构的大小。这减少了空间量,有助于使用额外的内存,仅仅是为了满足使用的命令(比如吞吐量) 

下面是具体的数据对比:

 

 

 

 

发布了83 篇原创文章 · 获赞 14 · 访问量 3万+
展开阅读全文

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

©️2019 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览