启动Spring Boot时,如果不设置内存参数会如何?

简介: 启动Spring Boot时,如果不设置内存参数会如何?

前言

最近正在进行从Spring Boot往Spring Cloud上改造升级。之前部署的应用程序比较少,还没什么问题。当Spring Cloud项目逐步新增之后,问题就爆发了,服务器内存不够用了。而现有的用户体量也没必要对服务器再次进行升级,于是就开始着手Spring Boot启动时JVM内存配置的优化。

服务现状

由于之前服务比较少,服务器资源充足,许多服务启动时都未添加JVM参数(遗留问题)。结果就是每个服务启动都占用了1.5G-2G的内存,有些服务的体量根本用不了这么多。那么,在Spring Boot中如果未设置JVM内存参数时,JVM内存是如何配置的呢?

JVM默认内存设置

当运行一个Spring Boot项目时,如果未设置JVM内存参数,Spring Boot默认会采用JVM自身默认的配置策略。在资源比较充足的情况下,开发者倒是不太用关心内存的设置。但一旦涉及到资源不足,JVM优化,那么就需要了解默认的JVM内存配置策略。

关于JVM内存最常见的设置为初始堆大小(-Xms)和最大堆内存(-Xmx)。很多人懒得去设置,而是采用JVM的默认值。特别是在开发环境下,如果启动的微服务比较多,内存会被撑爆。

而JVM默认内存配置策略分两种场景,大内存空间场景和小内存空间场景(小于192M)。

以4GB内存为例,初始堆内存大小和最大堆内存大小如下图:image.png默认情况下,最大堆内存占用物理内存的1/4,如果应用程序超过该上限,则会抛出OutOfMemoryError异常。初始堆内存大小为物理内存的1/64。

如果应用程序运行在手机上或物理内存小于192M时,JVM默认的初始堆内存大小和最大堆内存大小如下图:

image.png最大堆内存为物理内存的1/2,初始堆内存大小为物理内存的1/64,但当初始堆内存最小为8MB,则为8MB。

默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆到 -Xms的最小限制。因此,服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

其中最大堆内存是JVM使用内存的上限,实际运行过程中使用多少便是多少。默认,分配给年轻代的最大空间量是堆总大小的三分之一。

针对最开始的问题,如果每个程序都按照默认配置启动,一台服务器上部署多个应用时,就会出现内存吃紧的情况,造成一定的浪费。最简单的操作就是在执行java -jar启动时添加上对应的jvm内存设置参数。


java -Xms64m -Xmx128m -jar xxx.jar

切记参数要防止-jar参数之前。否则会被当做系统参数而无效。

当然在排查JVM的使用情况时,还会用到以下相关操作。

查看系统默认内存设置

通过上面的描述我们可以看到,不同的系统配置,JVM使用的内存是不同的。我们可以通过Java命令自带的功能来查看默认的内存设置。

在Linux操作系统下,输入如下命令:


java -XX:+PrintFlagsFinal -version | grep HeapSize

在Windows操作系统下,输入如下命令:


java -XX:+PrintFlagsFinal -version | findstr HeapSize

查看运行时内存情况

当应用程序运行时,如果我们想查看程序的运行情况,可通过以下几种方式来查询不同维度的数据。

查看正在运行的jvm服务

可以通过jps命令查看正在运行的jvm服务。





appledeMacBook-Pro:~ apple$ jps51972 EurekaApplication51973 Launcher51976 Jps

其中前面的数字为进程号。

查看某进程的JVM情况

可以使用jstat命令查询具体进程的GC情况。






appledeMacBook-Pro:~ apple$ jstat -gc 51972 5000 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT14336.0 14336.0  0.0   14312.7 145920.0 100055.3  175104.0   17500.2   52136.0 48999.3 7600.0 6958.7      9    0.058   2      0.050    0.10714336.0 14336.0  0.0   14312.7 145920.0 100055.3  175104.0   17500.2   52136.0 48999.3 7600.0 6958.7      9    0.058   2      0.050    0.10714336.0 14336.0  0.0   14312.7 145920.0 100055.3  175104.0   17500.2   52136.0 48999.3 7600.0 6958.7      9    0.058   2      0.050    0.107


其中51972是进程号,5000为刷新时间。

对应数据项的含义:

S0C:年轻代中第一个survivor(幸存区)的容量 (字节)

S1C:年轻代中第二个survivor(幸存区)的容量 (字节)

S0U:年轻代中第一个survivor(幸存区)目前已使用空间 (字节)

S1U:年轻代中第二个survivor(幸存区)目前已使用空间 (字节)

EC:年轻代中Eden(伊甸园)的容量 (字节)

EU:年轻代中Eden(伊甸园)目前已使用空间 (字节)

OC:Old代的容量 (字节)

OU:Old代目前已使用空间 (字节)

YGC:从应用程序启动到采样时年轻代中gc次数

YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)

FGC:从应用程序启动到采样时old代(全gc)gc次数

FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)

GCT:从应用程序启动到采样时gc用的总时间(s)


查看堆栈使用情况

通过jmap命令来查看堆栈的使用情况。



[root@bjb ~]# jmap -heap 10471Attaching to process ID 10471, please wait...Debugger attached successfully.Server compiler detected.JVM version is 25.262-b10
using thread-local object allocation.Parallel GC with 2 thread(s)
Heap Configuration:   MinHeapFreeRatio         = 0   MaxHeapFreeRatio         = 100   MaxHeapSize              = 2051014656 (1956.0MB)   NewSize                  = 42991616 (41.0MB)   MaxNewSize               = 683671552 (652.0MB)   OldSize                  = 87031808 (83.0MB)   NewRatio                 = 2   SurvivorRatio            = 8   MetaspaceSize            = 21807104 (20.796875MB)   CompressedClassSpaceSize = 1073741824 (1024.0MB)   MaxMetaspaceSize         = 17592186044415 MB   G1HeapRegionSize         = 0 (0.0MB)
Heap Usage:PS Young GenerationEden Space:   capacity = 342360064 (326.5MB)   used     = 128426952 (122.47748565673828MB)   free     = 213933112 (204.02251434326172MB)   37.51224675550943% usedFrom Space:   capacity = 4194304 (4.0MB)   used     = 3458288 (3.2980804443359375MB)   free     = 736016 (0.7019195556640625MB)   82.45201110839844% usedTo Space:   capacity = 4194304 (4.0MB)   used     = 0 (0.0MB)   free     = 4194304 (4.0MB)   0.0% usedPS Old Generation   capacity = 216006656 (206.0MB)   used     = 86142704 (82.15208435058594MB)   free     = 129863952 (123.84791564941406MB)   39.879652597371816% used
22325 interned Strings occupying 2361920 bytes.

关于具体参数就不在这里解释了。

小结

项目中往往一些被忽视的问题,深究起来,排查起来,反而能串联起来一系列的知识点和技能,或许这就是深入思考与探索的魅力所在。你在项目的使用过程中是否也遇到类似的问题,是否也深入探究过么?

目录
相关文章
|
2月前
|
Java 数据库连接 测试技术
SpringBoot入门 - 添加内存数据库H2
SpringBoot入门 - 添加内存数据库H2
80 3
SpringBoot入门 - 添加内存数据库H2
|
2月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
57 4
SpringBoot入门(4) - 添加内存数据库H2
|
2月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
358 1
|
13天前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
36 3
|
19天前
|
运维 监控 Ubuntu
【运维】如何在Ubuntu中设置一个内存守护进程来确保内存不会溢出
通过设置内存守护进程,可以有效监控和管理系统内存使用情况,防止内存溢出带来的系统崩溃和服务中断。本文介绍了如何在Ubuntu中编写和配置内存守护脚本,并将其设置为systemd服务。通过这种方式,可以在内存使用超过设定阈值时自动采取措施,确保系统稳定运行。
44 4
|
3月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
38 2
SpringBoot入门(4) - 添加内存数据库H2
|
2月前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
52 2
|
2月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
73 13
|
2月前
|
Java 数据库连接 测试技术
SpringBoot入门(4) - 添加内存数据库H2
SpringBoot入门(4) - 添加内存数据库H2
59 4
|
2月前
|
弹性计算 Kubernetes Perl
k8s 设置pod 的cpu 和内存
在 Kubernetes (k8s) 中,设置 Pod 的 CPU 和内存资源限制和请求是非常重要的,因为这有助于确保集群资源的合理分配和有效利用。你可以通过定义 Pod 的 `resources` 字段来设置这些限制。 以下是一个示例 YAML 文件,展示了如何为一个 Pod 设置 CPU 和内存资源请求(requests)和限制(limits): ```yaml apiVersion: v1 kind: Pod metadata: name: example-pod spec: containers: - name: example-container image:
273 1