线上系统部署的时候,JVM堆内存大小是越大越好吗?

2023-10-16 20:30

本文主要是介绍线上系统部署的时候,JVM堆内存大小是越大越好吗?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

“ 这篇文章,给大家聊一个生产环境的实践经验:线上系统部署的时候,JVM堆内存大小是越大越好吗

先说明白一个前提,本文主要讨论的是Kafka和Elasticsearch两种分布式系统的线上部署情况,不是普通的Java应用系统。

 

 

1、是否依赖Java系统自身内存处理数据?

 

先说明一点,不管是我们自己开发的Java应用系统,还是一些中间件系统,在实现的时候都需要选择是否基于自己Java进程的内存来处理数据。

 

大家应该都知道,Java、Scala等编程语言底层依赖的都是JVM,那么只要是使用JVM,就可以考虑在JVM进程的内存中来放置大量的数据。

 

还是给大家举个例子,大家应该还记得之前聊过消息中间件系统。

 

比如说系统A可以给系统B发送一条消息,那么中间需要依赖一个消息中间件,系统A要先把消息发送到消息中间件,然后系统B从这个消息中间件消费到这条消息。

 

大家看下面的示意图。

大家应该都知道,一条消息发送到消息中间件之后,有一种处理方式,就是把这条数据先缓冲在自己的JVM内存里。

 

然后过一段时间之后,再从自己的内存刷新到磁盘上去,这样可以持久化保存这条消息,如下图。

 

2、依赖Java系统自身内存有什么缺陷

 

如果用类似上述的方式,依赖Java系统自身内存处理数据,比如说设计一个内存缓冲区,来缓冲住高并发写入的大量消息,那么是有其缺陷的。

 

最大的缺陷,其实就是JVM的GC问题,这个GC就是垃圾回收,这里简单说一下他是怎么回事。

 

大家可以想一下,如果一个Java进程里老是塞入很多的数据,这些数据都是用来缓冲在内存里的,但是过一会儿这些数据都会写入磁盘。

 

那么写入磁盘之后,这些数据还需要继续放在内存里吗?

 

明显是不需要的了,此时就会依托JVM垃圾回收机制,把内存里那些不需要的数据给回收掉,释放掉那些内存空间腾出来。

 

但是JVM垃圾回收的时候,有一种情况叫做stop the world,就是他会停止你的工作线程,就专门让他进行垃圾回收。

 

这个时候,他在垃圾回收的时候,有可能你的这个中间件系统就运行不了了。

 

比如你发送请求给他,他可能都没法响应给你,因为他的接收请求的工作线程都停了,现在人家后台的垃圾回收线程正在回收垃圾对象。

 

大家看下图。

虽然说现在JVM的垃圾回收器一直在不断的演进和发展,从CMS到G1,尽可能的在降低垃圾回收的时候的影响,减少工作线程的停顿。

 

但是你要是完全依赖JVM内存来管理大量的数据,那在垃圾回收的时候,或多或少总是有影响的。

 

所以特别是对于一些大数据系统,中间件系统,这个JVM的GC(Garbage Collector,垃圾回收)问题,真是最头疼的一个问题。

 

 

 

3、优化为依赖OS Cache而不是JVM

 

所以类似Kafka、Elasticsearch等分布式中间件系统,虽然也是基于JVM运行的,但是他们都选择了依赖OS Cache来管理大量的数据。

 

也就是说,是操作系统管理的内存缓冲,而不是依赖JVM自身内存来管理大量的数据。

 

具体来说,比如说Kafka吧,如果你写一条数据到Kafka,他实际上会直接写入磁盘文件。

 

但是磁盘文件在写入之前其实会进入os cache,也就是操作系统管理的内存空间,然后过一段时间,操作系统自己会选择把他的os cache的数据刷入磁盘。

 

然后后续在消费数据的时候,其实也会优先从os cache(内存缓冲)里来读取数据。

 

相当于写数据和读数据都是依托于os cache来进行的,完全依托操作系统级别的内存区域来进行,读写性能都很高。

 

此外,还有另外一个好处,就是不要依托自身JVM来缓冲大量的数据,这样可以避免复杂而且耗时的JVM垃圾回收操作。

 

大家看下面的图,其实就是一个典型的Kafka的运行流程。

然后比如Elasticsearch,他作为一个现在最流行的分布式搜索系统,也是采用类类似的机制。

 

大量的依赖os cache来缓冲大量的数据,然后在进行搜索和查询的时候,也可以优先从os cache(内存区域)中读取数据,这样就可以保证非常高的读写性能。

 

 

 

4、老司机经验之谈:

 

依赖os cache的系统JVM内存越大越好?

 

所以现在就可以进入我们的主题了,那么比如就以上述说的kafka、elasticsearch等系统而言,在线上生产环境部署的时候,你知道他们是大量依赖于os cache来缓冲大量数据的。

 

那么,给他们分配JVM堆内存大小的时候是越大越好吗?

 

明显不是的,假如说你有一台机器,有32GB的内存,现在你如果在搞不清楚状况的情况下,要是傻傻的认为还是给JVM分配越大内存越好,此时比如给了16G的堆内存空间给JVM,那么os cache剩下的内存,可能就不到10GB了,因为本身其他的程序还要占用几个GB的内存。

 

那如果是这样的话,就会导致你在写入磁盘的时候,os cache能容纳的数据量很有限。

 

比如说一共有20G的数据要写入磁盘,现在就只有10GB的数据可以放在os cache里,然后另外10GB的数据就只能放在磁盘上。

 

此时在读取数据的时候,那么起码有一半的读取请求,必须从磁盘上去读了,没法从os cache里读,谁让你os cache里就只能放的下10G的一半大小的数据啊,另外一半都在磁盘里,这也是没办法的,如下图。

那此时你有一半的请求都是从磁盘上在读取数据,必然会导致性能很差。

 

所以很多人在用Elasticsearch的时候就是这样的一个问题,老是觉得ES读取速度慢,几个亿的数据写入ES,读取的时候要好几秒。

 

那能不花费好几秒吗?你要是ES集群部署的时候,给JVM内存过大,给os cache留了几个GB的内存,导致几亿条数据大部分都在磁盘上,不在os cache里,最后读取的时候大量读磁盘,耗费个几秒钟是很正常的。

 

 

 

5、正确的做法:

 

针对场景合理给os cache更大内存

 

所以说,针对类似Kafka、Elasticsearch这种生产系统部署的时候,应该要给JVM比如6GB或者几个GB的内存就可以了。

 

因为他们可能不需要耗费过大的内存空间,不依赖JVM内存管理数据,当然具体是设置多少,需要你精准的压测和优化。

 

但是对于这类系统,应该给os cache留出来足够的内存空间,比如32GB内存的机器,完全可以给os cache留出来20多G的内存空间,那么此时假设你这台机器总共就写入了20GB的数据,就可以全部驻留在os cache里了。

 

然后后续在查询数据的时候,不就可以全部从os cache里读取数据了,完全依托内存来走,那你的性能必然是毫秒级的,不可能出现几秒钟才完成一个查询的情况。

 

整个过程,如下图所示:

所以说,建议大家在线上生产系统引入任何技术的时候,都应该先对这个技术的原理,甚至源码进行深入的理解,知道他具体的工作流程是什么,然后针对性的合理设计生产环境的部署方案,保证最佳的生产性能。

这篇关于线上系统部署的时候,JVM堆内存大小是越大越好吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/220600

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2