Java堆外缓存(一个很有意思的应用)
我们在开发过程中会遇到这样的场景:就是一个服务的各项 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堆外缓存(一个很有意思的应用)的更多相关文章
- Java堆外内存之一:堆外内存场景介绍(对象池VS堆外内存)
最近经常有人问我在Java中使用堆外(off heap)内存的好处与用途何在.我想其他面临几样选择的人应该也会对这个答案感兴趣吧. 堆外内存其实并无特别之处.线程栈,应用程序代码,NIO缓存用的都是堆 ...
- Java堆外内存之三:堆外内存回收方法
一.JVM内存的分配及垃圾回收 对于JVM的内存规则,应该是老生常谈的东西了,这里我就简单的说下: 新生代:一般来说新创建的对象都分配在这里. 年老代:经过几次垃圾回收,新生代的对象就会放在年老代里面 ...
- 一文深入了解史上最强的Java堆内缓存框架Caffeine
它提供了一个近乎最佳的命中率.从性能上秒杀其他一堆进程内缓存框架,Spring5更是为了它放弃了使用多年的GuavaCache 缓存,在我们的日常开发中用的非常多,是我们应对各种性能问题支持高并发的一 ...
- Java堆外内存管理
Java堆外内存管理 1.JVM可以使用的内存分外2种:堆内存和堆外内存: 堆内存完全由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemo ...
- Java堆外内存之二:堆外内存使用总结
目录: <堆外内存操作类ByteBuffer> <DirectBuffer> <Unsafe(java可直接操作内存(),挂起与恢复,CAS操作)> 有时候对内存进 ...
- google-perftools 分析JAVA 堆外内存
google-perftools 分析JAVA 堆外内存 分类: j2se2011-08-25 21:48 3358人阅读 评论(4) 收藏 举报 javahbasehtml工具os 原文转自:htt ...
- JDBC数据源(DataSource)数据源技术是Java操作数据库的一个很关键技术,流行的持久化框架都离不开数据源的应用。
JDBC数据源(DataSource)的简单实现 数据源技术是Java操作数据库的一个很关键技术,流行的持久化框架都离不开数据源的应用. 2.数据源提供了一种简单获取数据库连接的方式,并能在内部通 ...
- 实战经验 | Cassandra Java堆外内存排查经历全记录
背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测.压测时候比较容易触发OOM Killer,把cassandra进程干掉.问题是8G这个规格我配置的heap(Xmx ...
- 超干货!Cassandra Java堆外内存排查经历全记录
背景 最近准备上线cassandra这个产品,同事在做一些小规格ECS(8G)的压测.压测时候比较容易触发OOM Killer,把cassandra进程干掉.问题是8G这个规格我配置的heap(Xmx ...
- Netty之Java堆外内存扫盲贴
Java的堆外内存本来是高贵而神秘的东西,只在一些缓存方案的收费企业版里出现.但自从用了Netty,就变成了天天打交道的事情,毕竟堆外内存能减少IO时的内存复制,不需要堆内存Buffer拷贝一份到直接 ...
随机推荐
- 如何通过Java导出带格式的 Excel 数据到 Word 表格
在Word中制作报表时,我们经常需要将Excel中的数据复制粘贴到Word中,这样则可以直接在Word文档中查看数据而无需打开另一个Excel文件.但是如果表格比较长,内容就会存在一定程度的丢失,无法 ...
- SolidEdge ST8安装教程
SolidEdge ST8安装教程: 1.使用百度云客户端下载Solidedge ST8软件安装包,打开软件安装文件夹: 2.选择.ISO安装文件,打开.ISO安装文件,可以解压或使用虚拟光驱加载: ...
- 5种典型 API 攻击及预防建议
API 帮助跨多个设备互连多个应用程序或软件系统,定义它们可以发出的调用或请求的种类.调用的方式.应使用的数据格式以及应遵守的约定.API 已经发展成为重要的互连,支持不同应用程序架构之间的通信,促进 ...
- 树莓派编译opencv4
前言 我用的是 树莓派3b 编译的 opencv4.1.0,如果不想编译可以直接下载我编译好的. 下载地址 直接 make install,或者按照我后续步骤复制动态链接库. 准备 需要调节虚拟内存大 ...
- Leanote蚂蚁笔记-私人云笔记服务器搭建
title: Leanote蚂蚁笔记-私人云笔记服务器搭建 date: 2020-02-22 21:53:24 categories: - [IT,技术] - [IT,软件,程序] - [IT,软件, ...
- gulp4.0构建任务
执行default任务时,依次执行以下任务 gulp.task('default', ['htmlmin', 'cssmin', 'jsmin', 'copy']); 报错:Task function ...
- 预编译SQL为什么能够防止SQL注入
前言 之前我一个搞网络安全的朋友问了我一个的问题,为啥用 PreparedStatement 预编译的 SQL 就不会有被 SQL 注入的风险? 第一时间我联想到的是八股文中关于 Mybatis 的脚 ...
- Linux开发板连接WPA加密的AP路由器
Linux目前有两种方法配置网络: wireless-tools wpa_supplicant iw支持的驱动较多,但只支持WEP加密:wpa_supplicant有部分驱动支持不完善,但支持WEP. ...
- socet网络编程
一. 网络模型 应用层 ----- HTTP协议 传输层 ----- TCP协议 网络层 ----- IP协议 链路层 ----- 以太网协议 二. ...
- MySQL字符编码、存储引擎、严格模式、字段类型之浮点 字符串 枚举与集合 日期类型
目录 字符编码与配置文件 数据路储存引擎 创建表的完整语法 字段类型之整型 严格模式 字段类型之浮点型 字段类型之字符串类型 数字的含义 字段类型之枚举与集合 字段类型之日期类型 字符编码与配置文件 ...