让我们通过查看应用程序来说明NMT的使用,在这种情况下,我们的老朋友Petclinic。下面的饼图显示了当使用48MB最大堆(-Xmx48M)启动Petclinic时由NMT报告的JVM的内存使用量(减去其自身的开销):
正如您所看到的,非堆内存占绝大多数JVM的内存使用量,堆内存仅占总数的六分之一。在这种情况下,大约44MB(垃圾收集后立即使用33MB)。非堆内存使用总量为223MB。
-
压缩类空间:用于存储有关已加载的类的信息。受到约束MaxMetaspaceSize。已加载的类数的函数。
-
线程:JVM中线程使用的内存。正在运行的线程数的函数。
-
代码缓存:JIT用于存储其输出的内存。已加载的类数的函数。受到约束ReservedCodeCacheSize。可以通过调整JIT来减少,例如,禁用分层编译。
-
GC:存储GC使用的数据。根据使用的垃圾收集器而有所不同。
-
符号:存储符号,如字段名称,方法签名和实习字符串。过多的符号内存使用情况可能表明字符串过于激进。
-
内部:存储不适合任何其他区域的其他内部数据。
与堆内存相比,非堆内存在负载下不太可能发生变化。一旦应用程序加载了它将使用的所有类并且JIT完全预热,事情就会陷入稳定状态。要查看压缩类空间使用量的减少,加载类的类加载器需要进行垃圾回收。在将应用程序部署到servlet容器或应用程序服务器时,这种情况更常见 - 应用程序的类加载器将在取消部署应用程序时进行垃圾收集 - 但现代应用程序部署方法很少发生。
配置JVM以有效利用给定数量的可用RAM并不容易。如果您启动JVM -Xmx16M并期望它最多可以使用16MB的RAM,那么您会感到非常惊讶。
调整JVM大小的一个有趣的方面是JIT的代码缓存。默认情况下,HotSpot JVM最多可使用240MB。如果代码缓存太小,JIT将耗尽空间来存储其输出,因此性能将受到影响。如果缓存太大,可能会浪费内存。在调整代码缓存大小时,查看应用程序内存使用情况及其性能的影响非常重要。
在Docker容器中运行时,Java的最新版本现在知道容器的内存限制并尝试相应地调整JVM的大小。不幸的是,这种大小调整经常过度分配非堆内存并且分配不足。假设您有一个在具有2个CPU和512MB可用内存的容器中运行的应用程序。您希望它能够处理更多负载,因此您将CPU加倍为4,将内存加倍至1GB。如上所述,堆使用通常根据负载而变化,而非堆使用则更少。因此,我们希望将大部分额外的512MB内存提供给堆来应对增加的负载。不幸的是,JVM默认情况下不会这样做,并且会在其堆和非堆区域之间更均等地分配额外的内存。
值得庆幸的是,CloudFoundry团队拥有丰富的JVM内存占用知识。如果您要将应用程序推送到CloudFoundry,构建包将自动为您应用此知识。
考虑到堆和非堆内存使用情况,我们花了很多时间在Spring团队中考虑性能和内存利用率。限制非堆内存使用的一种方法是使Framework的一部分尽可能通用。这方面的一个例子是使用反射来创建应用程序的bean并将其注入到应用程序的bean中。由于使用了反射,所使用的Framework代码量保持不变,无论应用程序包含多少bean。我们使用基于堆的缓存来优化启动时间,一旦启动完成就清除此缓存。然后,垃圾收集器可以轻松地回收堆内存,从而在应用程序处理其工作负载时为应用程序提供尽可能多的内存。
完结
Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
[外链图片转存中…(img-4CW0jv7k-1718729214691)]