JVM内存模型
先复习一波JVM的内存模型,线程共享的区域为堆、方法区|永久代,线程不共享的区域为栈、程序计数器。
对象创建的整体流程可以用一图描述
内存逃逸
- 逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。
- 当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。
- 通俗点讲,如果一个对象的指针被多个方法或者线程引用时,那么我们就称这个对象的指针发生了逃逸
- 逃逸分析的好处
-
- 栈上分配,可以降低垃圾收集器运行的频率。
- 同步消除,如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步。
- 标量替换,把对象分解成一个个基本类型,并且内存分配不再是分配在堆上,而是分配在栈上。这样的好处有,一、减少内存使用,因为不用生成对象头。二、程序内存回收效率高,并且GC频率也会减少
TLAB
- 每个线程在Java堆中预先分配一小块内存,然后再给对象分配内存的时候,直接在自己这块”私有”内存中分配,当这部分区域用完之后,再分配新的”私有”内存。
- TLAB是虚拟机在堆内存的eden划分出来的一块专用空间,是线程专属的。在虚拟机的TLAB功能启动的情况下,在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。
- TLAB是堆内存的一部分,他在读取上确实是线程共享的,但是在内存分分配上,是线程独享的。。
排除内存逃逸外,创建的对象都存放在堆内,使用堆内空间就要受制于JVM垃圾回收的管控,稍有不慎就会oom。
假如系统里就是需要大量本地数据缓存如何避免oom呢。
堆外内存
了解netty的朋友肯定熟悉这个概念,堆外缓存顾名思义不存放在堆中,摆脱JVM管控就不会收到gc的影响。
OHC
市场上成熟且高性能的当属开源堆外缓存组件OHC了。OHC全称为off-heap-cache,即堆外缓存,是2015年针对ApacheCassandra开发的缓存框架,后来从 Cassandra 项目中独立出来,成为单独的类库,其项目地址为
https://github.com/snazy/ohc 。
特性
- 数据存储在堆外,只有少量元数据存储堆内,不影响 GC
- 支持为每个缓存项设置过期时间
- 支持配置 LRU、W_TinyLFU 驱逐策略
- 能够维护大量的缓存条目
- 支持异步加载缓存
- 读写速度在微秒级别
如何使用
// 序列化方式需要使用者手动实现 OHCache<String, String> ohCache = OHCacheBuilder.<String, String>newBuilder() .keySerializer(new StringSerializer()) .valueSerializer(new StringSerializer()) .eviction(Eviction.LRU) .build(); ohCache.put("hello", "world");