我们在开发过程中会遇到这样的场景:就是一个服务的各项 JVM 的配置都比较合理的情况下,它的 GC 情况还是不容乐观。分析之后发现有 2 个对象特别巨大,占了总存活堆内存的 90%以上。其中第 1 大对象是本地缓存, GC 之后对象一直存活。然后不久应用就会抛出OutOfMemoryError,那怎样避免这种情况呢?

这次给大家推荐一个比较好用的技术:堆外缓存。哈哈哈,顾名思义就是在Java堆之外的缓存,也就是把一些大的难以被GC回收的对象放到堆之外。PS堆外内存不受,堆内内存大小的限制,只受服务器物理内存的大小限制。这三者之间的关系是这样的:物理内存=堆外内存+堆内内存。

技术大佬的GITHUB地址:https://github.com/snazy/ohc  。

要使用他的技术就先要引用对应的jar包,Maven坐标如下:

<dependency>
<groupId>org.caffinitas.ohc</groupId>
<artifactId>ohc-core</artifactId>
<version>0.7.4</version>
</dependency>
//大神给的使用方式如下:
//Quickstart:
OHCache ohCache = OHCacheBuilder.newBuilder()
.keySerializer(yourKeySerializer)
.valueSerializer(yourValueSerializer)
.build();

上面是Quickstart 看起来使用如此的丝滑(简单),但是上面的代码是填空题,我们看到复制粘贴后代码不能使用后开始。。。。。。此处省略一万字。其实大神写的代码怎么不能用的呢,不要怀疑大神一定是自己的方法不对,我们的口号是?

如果提供的代码复制粘贴不能直接用的工程师不能称之为大神工程师。

但是github上的大神写的东西怎么不能直接用呢?一定是你的思路不对,老司机都知道,大神写代码一定有写单元测试的,要不然不会有那么多人用的(所以要想成为大神单元测试一定要写好),所以把代码拉下来,复制单元测试的东西应该能直接使用 。下面是CTRL+C来的代码。

public static void main(String[] args) {
OHCache ohCache = OHCacheBuilder.<String, String>newBuilder()
.keySerializer(new StringSerializer())
.valueSerializer(new StringSerializer())
.build();
ohCache.put("name","xiaozhang");
System.out.println(ohCache.get("name")); // 结果 xiaozhang
}
static class StringSerializer implements CacheSerializer<String>{
@Override
public void serialize(String value, ByteBuffer buf) {
// 得到字符串对象UTF-8编码的字节数组
byte[] bytes = value.getBytes(Charsets.UTF_8);
// 用前16位记录数组长度
buf.put((byte) ((bytes.length >>> 8) & 0xFF));
buf.put((byte) ((bytes.length) & 0xFF));
buf.put(bytes);
}
@Override
public String deserialize(ByteBuffer buf) {
// 判断字节数组的长度
int length = (((buf.get() & 0xff) << 8) + ((buf.get() & 0xff)));
byte[] bytes = new byte[length];
// 读取字节数组
buf.get(bytes);
// 返回字符串对象
return new String(bytes, Charsets.UTF_8);
}
@Override
public int serializedSize(String value) {
byte[] bytes = value.getBytes(Charsets.UTF_8);
// 设置字符串长度限制,2^16 = 65536
if (bytes.length > 65536)
throw new RuntimeException("encoded string too long: " + bytes.length + " bytes");
// 设置字符串长度限制,2^16 = 65536
return bytes.length + 2;
}
}

上面的代码我们看到这家伙类似Java中的Map 。也就是一个key和value 结构的对象。感觉So easy ,没什么大的用途。简单?那是你想简单了,后面大招来了。

很简单的代码演示如下:

public class MapCasheTest {
static HashMap<String,String> map = new HashMap<>();
public static void main(String[] args) throws Exception {
oomTest();
}
private static void oomTest() throws Exception{
// 休眠几秒,便于观察堆内存使用情况
TimeUnit.SECONDS.sleep(30);
int result = 0 ;
while (true){
String string = new String(new byte[1024*1024]) ;
map.put(result+"",string) ;
result++;
}
}
}

运行一小会就报这个错了,也是文章中刚开始说的那个错误。

然后我们去监控系统的堆栈使用情况如下图(只用一小会就把堆内存快用满了,然后自然系统就报错了)

下面我们使用同样的逻辑写如下代码,很神奇的事情出现了,大跌眼镜的事情出现了,先亮出代码如下:

public static void main(String[] args) throws Exception{
TimeUnit.SECONDS.sleep(30);
OHCache ohCache = OHCacheBuilder.<String, String>newBuilder()
.keySerializer(new Test.StringSerializer())
.valueSerializer(new Test.StringSerializer())
.build();
int result = 0 ;
while (true){
String string = new String(new byte[2048*2048]) ;
ohCache.put(result+"",string) ;
result++;
}
}

程序一直很稳定的运行,没有报错,堆栈运行一上一下,至少程序没报错:

为什么会出现这种情况呢?因为文章开始就讲了这个用的是堆外内存,也就是用的自己电脑的内存,自己电脑的内存目前普通的电脑也有2个G那么大,所以程序一直运行稳定。下面看看我们CPU运行的情况,刚开始有点高,当我把程序关闭CPU使用立马下来了。

哈哈哈,如果是自己测试的时候记得要把自己的工作相关的东西都先保存了,免得你的电脑内存太小,有可能会造成电脑关机。

那么我们在什么情况会使用这个大神的工具呢?就是自己程序有大的对象,并且GC一直无法把这个大对象回收,就可以使用上面的方法,能保证程序稳定运行。具体OH大神是怎么实现这种方式的呢?本文不做研究,他用的技术太深了。

最近ChatGPT比较火,我也问了些问题,回得很好,给个赞。你如果有啥想问的我也可以帮你问问它。

欢迎关注微信公众号:程序员xiaozhang 。会更新更多精彩内容。

Java堆外缓存(一个很有意思的应用)的更多相关文章

  1. Java堆外内存之一:堆外内存场景介绍(对象池VS堆外内存)

    最近经常有人问我在Java中使用堆外(off heap)内存的好处与用途何在.我想其他面临几样选择的人应该也会对这个答案感兴趣吧. 堆外内存其实并无特别之处.线程栈,应用程序代码,NIO缓存用的都是堆 ...

  2. Java堆外内存之三:堆外内存回收方法

    一.JVM内存的分配及垃圾回收 对于JVM的内存规则,应该是老生常谈的东西了,这里我就简单的说下: 新生代:一般来说新创建的对象都分配在这里. 年老代:经过几次垃圾回收,新生代的对象就会放在年老代里面 ...

  3. 一文深入了解史上最强的Java堆内缓存框架Caffeine

    它提供了一个近乎最佳的命中率.从性能上秒杀其他一堆进程内缓存框架,Spring5更是为了它放弃了使用多年的GuavaCache 缓存,在我们的日常开发中用的非常多,是我们应对各种性能问题支持高并发的一 ...

  4. Java堆外内存管理

    Java堆外内存管理   1.JVM可以使用的内存分外2种:堆内存和堆外内存: 堆内存完全由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemo ...

  5. Java堆外内存之二:堆外内存使用总结

    目录: <堆外内存操作类ByteBuffer> <DirectBuffer> <Unsafe(java可直接操作内存(),挂起与恢复,CAS操作)> 有时候对内存进 ...

  6. google-perftools 分析JAVA 堆外内存

    google-perftools 分析JAVA 堆外内存 分类: j2se2011-08-25 21:48 3358人阅读 评论(4) 收藏 举报 javahbasehtml工具os 原文转自:htt ...

  7. JDBC数据源(DataSource)数据源技术是Java操作数据库的一个很关键技术,流行的持久化框架都离不开数据源的应用。

    JDBC数据源(DataSource)的简单实现   数据源技术是Java操作数据库的一个很关键技术,流行的持久化框架都离不开数据源的应用. 2.数据源提供了一种简单获取数据库连接的方式,并能在内部通 ...

  8. 实战经验 | Cassandra Java堆外内存排查经历全记录

    背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测.压测时候比较容易触发OOM Killer,把cassandra进程干掉.问题是8G这个规格我配置的heap(Xmx ...

  9. 超干货!Cassandra Java堆外内存排查经历全记录

    背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测.压测时候比较容易触发OOM Killer,把cassandra进程干掉.问题是8G这个规格我配置的heap(Xmx ...

  10. Netty之Java堆外内存扫盲贴

    Java的堆外内存本来是高贵而神秘的东西,只在一些缓存方案的收费企业版里出现.但自从用了Netty,就变成了天天打交道的事情,毕竟堆外内存能减少IO时的内存复制,不需要堆内存Buffer拷贝一份到直接 ...

随机推荐

  1. C#字典出错“集合已经修改,可能无法执行枚举操作”

    出现这个现象的原因是由于线程安全考虑,如果你边对字典循环,又同时移除字典中的某个键值对, 那么将会出现这种错误,解决这种问题的方法是你没次remove某个键值对后需要break结束对字典的循环.

  2. 我把 CPU 三级缓存的秘密,藏在这 8 张图里

    本文已收录到  GitHub · AndroidFamily,有 Android 进阶知识体系,欢迎 Star.技术和职场问题,请关注公众号 [彭旭锐] 进 Android 面试交流群. 前言 大家好 ...

  3. Postman使用指导

    1. 下载好Postman,不用注册账号,直接离线使用 2. 软件的配置修改: 2.1关闭SSL认证 2.2关闭系统代理 2.3上传证书填写hosthost为服务器地址和端口:crt和key可以通过国 ...

  4. 专业的C头文件设计和重构指南

    头文件设计要点: 1. 头文件注释 2. guard define 3. 尽量不要在头文件中暴露数据结构 4. 要自包含,保证头文件独立编译和功能正确 5. 函数声明前加XXX_API利于拓展 6.  ...

  5. 自学 TypeScript 第三天 使用webpack打包 TS 代码

    前言: 大家好啊,昨天介绍了 TS 编译器的配置,但在我们实际开发当中直接使用 TS 编译器去编译代码的情况会有,但没有很多,因为我们在开发大型项目的时候,一般我们都会用到打包工具,也不可能脱离打包工 ...

  6. VS 新版本无法打开旧项目问题处理

    问题 最近想阅读 WorkflowCore 的源码,苦于代码量巨大,就想将项目回退到 Init Commit 版本 但是在回退版本后,工程内Project 显示已卸载 重新加载后 提示: 不支持 Th ...

  7. nm命令解释

    nm命令参数解释 -A 或-o或 --print-file-name:打印出每个符号属于的文件-a或--debug-syms:打印出所有符号,包括debug符号-B:BSD码显示-C或--demang ...

  8. Azure DevOps Server 用户组加入 Azure AD Domain Service 管理用户

    一,引言 今天我们继续讲解 Azure DevOps Server 的内容,对于管理用户组除了在 Azure DevOps Server 服务器上添加管理员方式外,还有没有其他方式,Azure Dev ...

  9. 关于linux上mysql导出excel 文件

    这里简单介绍两种方法导出 1.在mysql交互中 首先查看"secure_file_priv"变量 SHOW VARIABLES LIKE "secure_file_pr ...

  10. 重新认识下JVM级别的本地缓存框架Guava Cache(3)——探寻实现细节与核心机制

    大家好,又见面了. 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面.如果感兴趣,欢迎关注以获取后续更新. 通过<重新认识下JVM级别的本地 ...