JVM调优-方法区,堆,栈调优详解

简介: JVM调优-方法区,堆,栈调优详解

通常我们都知道在堆空间新生代Eden区满了,会触发minor GC, 在老年代满了会触发full GC, 触发full GC会导致Stop The World, 那你们知道还有一个区域满了一会触发Full GC么?而且这个区域满了会直接影响我们的开发效率。


一、方法区参数调优



我们可以对运行时数据区的内存进行参数设置. 这是jvm调优的重点. 参数的变化将影响到整体效率

1187916-20200706125359526-1299569980.png


核心参数设置如下:


java -Xms2048M -Xmx1024M -Xss512k -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -jar microservice-eureka-server.jar

这是一个通用的设置。图中具体含义如下:


  • -Xms:堆空间最小值
  • -Xmx:堆空间最大值
  • -Xmn:新生代占堆空间的大小
  • -XX:MetaspaceSize:方法区(元空间)初始值
  • -XX:MaxMetaspaceSize:方法区(元空间)最大值
  • -Xss:每一个线程的空间大小

下面主要研究方法区参数设置


1. 方法区(元空间)参数设置

1187916-20200707055715388-403856869.png


在jdk8之前有个区域叫做永久代, 在jdk8及以后改名字了, 叫做元空间. 这块内存空间占用的是直接的物理内存.


元空间有一个特点: 可以动态扩容。如果, 我们没有设置元空间的上限, 那么他可以扩大到整个内存. 比如内存条是8G的, 堆和栈分配了4G的空间, 那么元空间最多可以使用4G。

我们可以通过参数来设置使用的元空间内存。


对于64位的JVM来说, 元空间默认大小是21M, 元空间的默认最大值是无上限的, 他的上限就是内存空间


  • -XX:MetaspaceSize: 元空间的初始空间大小, 以字节位单位, 默认是21M,达到该值就会触发full GC, 同时收集器会对该值进行调整, 如果释放了大量的空间, 就适当降低该值, 如果释放了很少的空间, 提升该值,但最到不超过-XX:MaxMetaspaceSize设置的值
比如: 
初始值是21M, 第一次回收了20M, 那么只有1M没有被回收, 下一次, 元空间会自动调整大小, 可能会调整到15M
初始大小依然是21M, 第二次回收发现回收了1M, 有20M没有被回收, 他就会自动扩大空间, 可能扩大到30M,也可能是40M
  • -XX:MaxMetaspaceSize: 设置元空间的最大值, 默认是-1, 即不限制, 或者说只受限于本地内存的大小


由于调整元空间的大小需要full GC, 这是非常昂贵的操作, 如果应用在启动的时候发生大量的full GC, 通常都是由于永久代或元空间发生了大小的调整, 基于这种情况, 一般建议在JVM参数中将-XX:MetaspaceSize和-XX:MaxMetaspaceSize设置成一样的值, 并设置的比初始值还要大, 对于8G物理内存的机器来说, 一般会将这两个值设置为256M或者512M都可以


2. 建议设置元空间值, 如果不设置会怎么样?


不设置元空间默认就是21M, 很容易就会放满, 通常我们的war可能都是几十M, 甚至几个G. 如果我们在启动程序的时候, 会启动几分钟. 这很有可能是没有设置元空间的大小.

放满后会发生full GC, 然后在扩大一点元空间, 扩大到25M, 重新开始, 过了一会又放满了, 再次full GC, 在扩大一点, 元空间扩大到30M, 就这样一直发生full GC, 然后一直扩大元空间, 直到扩大的元空间大小合适, 不再发生full gc, 程序才会正常启动运行. 这是个很耗时耗性能的操作, 这样的full GC也是没有必要的.


如果项目启动较慢,多次重复启动,考虑是不是元空间设置不合理,或者内存不够导致。


二. 线程栈参数调优



-Xss512k:设置栈空间参数的

这个参数就是用来设置栈空间的. 他是设置的一个线程栈占用的空间, 一个程序启动后可能有多个线程栈, 那么他们占用的空间都是512k。


一个程序启动以后,系统为其分配的栈空间是固定的。理论上来说,如果每一个线程占用的空间少,那么就能分配更多的线程。否则则相反。但这也不是确定的,还有和系统的cpu,内存有关系。


当线程分配的空间用完的时候,就会抛出栈溢出异常。下面来看一个例子

package com.lxl.jvm;
public class StackOverflowTest {
    /**
     * jvm 设置-Xss128M, (默认是1M)
     */
    static int count = 0;
    public static void redo(){
        count ++;
        redo();
    }
    public static void main(String[] args) {
        try {
            redo();
        }catch (Throwable e) {
            e.printStackTrace();
            System.out.println(count);
        }
    }
}


这里定义了一个变量count, main方法里调用了redo()方法. 当我们执行main方法的时候, 线程栈模型是什么样的呢?

1187916-20211009113004291-1024778648.png

当程序执行到main方法的时候, 会在线程栈中开辟一个main方法的栈帧

继续执行, 执行到redo()的时候, 会在线程栈在开辟一块redo方法栈帧

redo方法里又调用了redo方法. 继续开辟一块redo方法栈帧,

.......

栈帧是占用内存空间的. 总有一个时刻会把栈内存消耗完. 就会报栈内存溢出了


image.png


我们看到程序一共运行了16979次发生了栈溢出.


当栈空间设置的小一些呢?比如256k

1187916-20200707093109698-116793075.png


我们运行看效果

image.png


当运行到2079次的时候, 发生了栈溢出。

相关文章
|
13天前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
28 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
8天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
6天前
|
监控 Java 编译器
Java虚拟机调优实战指南####
本文深入探讨了Java虚拟机(JVM)的调优策略,旨在帮助开发者和系统管理员通过具体、实用的技巧提升Java应用的性能与稳定性。不同于传统摘要的概括性描述,本文摘要将直接列出五大核心调优要点,为读者提供快速预览: 1. **初始堆内存设置**:合理配置-Xms和-Xmx参数,避免频繁的内存分配与回收。 2. **垃圾收集器选择**:根据应用特性选择合适的GC策略,如G1 GC、ZGC等。 3. **线程优化**:调整线程栈大小及并发线程数,平衡资源利用率与响应速度。 4. **JIT编译器优化**:利用-XX:CompileThreshold等参数优化即时编译性能。 5. **监控与诊断工
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
63 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
17天前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
25天前
|
Arthas 监控 数据可视化
JVM进阶调优系列(7)JVM调优监控必备命令、工具集合|实用干货
本文介绍了JVM调优监控命令及其应用,包括JDK自带工具如jps、jinfo、jstat、jstack、jmap、jhat等,以及第三方工具如Arthas、GCeasy、MAT、GCViewer等。通过这些工具,可以有效监控和优化JVM性能,解决内存泄漏、线程死锁等问题,提高系统稳定性。文章还提供了详细的命令示例和应用场景,帮助读者更好地理解和使用这些工具。
|
30天前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
43 3
|
1月前
|
算法 Java
JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
本文详细介绍了JVM中的GC算法,包括年轻代的复制算法和老年代的标记-整理算法。复制算法适用于年轻代,因其高效且能避免内存碎片;标记-整理算法则用于老年代,虽然效率较低,但能有效解决内存碎片问题。文章还解释了这两种算法的具体过程及其优缺点,并简要提及了其他GC算法。
 JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
|
1月前
|
Java
JVM进阶调优系列(5)CMS回收器通俗演义一文讲透FullGC
本文介绍了JVM中CMS垃圾回收器对Full GC的优化,包括Stop the world的影响、Full GC触发条件、GC过程的四个阶段(初始标记、并发标记、重新标记、并发清理)及并发清理期间的Concurrent mode failure处理,并简述了GC roots的概念及其在GC中的作用。