Java必知必会系列:性能调优与内存管理

作者:禅与计算机程序设计艺术

1.简介

随着互联网、移动互联网、电子支付等应用的普及,基于Web开发的企业级应用已经成为一种非常主流的开发模式。对于如何高效地开发出具有良好用户体验和高性能的Web应用程序,无论从技术实现还是服务端架构都需要进行一些优化和改进。在这种背景下,Java已成为市场上最受欢迎的语言之一,其作为一个高性能、跨平台、功能强大的编程语言正在迅速崛起。这不仅使得Java成为最佳的“选择”,更重要的是它所提供的丰富的API和工具库为开发人员提供了构建健壮、稳定的应用程序的能力。因此,本专栏将以Java语言为主线,通过系统的学习Java性能调优技巧和内存管理知识点,帮助读者深刻理解Java在Web开发中的作用及工作原理。

作者简介:莫烦(俗称码农),目前就职于微软中国区一线研发部,负责技术架构、研发,喜欢研究新技术,精通Java编程语言和数据库相关技术。除了日常工作外,他也是一个开源爱好者,参与开源项目如Hutool和Mybatis-Plus并做了一些贡献。

2.基础知识

2.1 Java虚拟机

Java是一门面向对象的语言,既可以用于编写桌面应用程序,也可以用于开发Web应用程序。为了实现跨平台特性,Java编译器编译生成字节码文件,由Java虚拟机(JVM)执行字节码。JVM运行时将字节码文件转换成操作系统能够识别的机器指令,这样就可以让不同系统上的程序共用同一个Java运行环境,从而实现跨平台特性。JVM包括类加载器、堆内存、方法区、垃圾回收、JIT编译等模块。

2.2 JVM内存结构

JVM内存结构分为五个部分:方法区、堆内存、虚拟机栈、本地方法栈、程序计数器。其中,程序计数器是当前线程所执行的字节码行号指示器;虚拟机栈是执行引擎运行时的数据区域,每个方法被执行的时候,一个栈帧就产生,保存局部变量表、操作数栈、动态链接、方法出口信息。每个栈帧中都包含以下部分:

1.局部变量表:用于存放方法的参数和局部变量。当方法调用时,参数值如果没有变化,则局部变量表中的参数不会被复制到新的栈帧中,这样可以节省内存空间。

2.操作数栈:也叫表达式栈,用于存放计算过程中间结果。一条Bytecode指令在执行的时候,可能会产生一个或者多个操作数,这些操作数就是保存在这个栈中,例如,加法运算符+需要两个操作数才能完成。

3.动态链接:指向运行时常量池的方法引用。每个栈帧在创建的时候,都会被赋予一个指针,指向运行时常量池中的方法引用。当方法调用时,根据该方法引用找到实际的目标方法,并且更新栈帧数据。

4.返回地址:方法正常退出或者异常退出后的跳转地址。每当方法执行完毕后,JVM都会释放对应的栈帧,并在调用该方法的栈帧中记录返回地址,以便回到方法调用前继续执行。

5.方法区:主要用于存储已被加载的类的元数据信息、静态变量、常量、编译器编译后的代码等数据。由于方法区属于堆外内存,所以其大小比较难确定,一般取决于系统可用内存大小和各个类的体积。

2.3 对象创建流程

对象创建一般有如下几个阶段:

  1. 类检查:首先判断要创建的对象是否是某个类的实例。

  2. 分配内存:JVM根据对象的数据类型确定内存的分配方式。比如,如果是boolean型的,那么只需一个bit的空间,如果是int型的,那么需要四个byte的空间。JVM还会根据预期的内存大小来设置最小内存分配单元,然后进行内存分配。

  3. 初始化零值:对象内存被分配后,JVM会初始化零值,比如,整型变量的值默认为0,布尔型变量值为false。

  4. 执行构造函数:如果构造函数存在的话,那么此时就会执行构造函数对成员变量进行赋值操作,比如,对象属性默认值或通过其他方式初始化。

  5. 返回引用:最后,JVM会返回对象的引用。对于之前创建过的对象来说,返回的引用就是之前分配的那块内存。

2.4 JVM优化策略

JVM性能调优常用的优化手段有:

  1. 编译优化:优化编译器的编译选项,提升编译速度,减少生成的代码量,从而提升JVM的运行速度。

  2. GC优化:调整GC算法,减小GC停顿时间,从而提升JVM的吞吐量。

  3. 内存优化:压缩堆内存、增大Heap空间,减少FullGC频率。

  4. 线程优化:减少线程数量、减小线程切换开销,提升并发处理能力。

  5. 第三方库选择:选择合适的第三方库,降低开发难度,提升性能。

3.GC(Garbage Collection)机制

GC是JVM自动内存管理的一种算法。JVM通过GC算法自动管理内存,确保无用对象内存空间被回收,同时避免内存泄漏和内存溢出的问题。JVM中实现了不同的GC算法,其中最著名的有两种:引用计数法和标记-清除算法。

3.1 引用计数法

引用计数法是老生代垃圾收集算法的古老代表,其主要思路是为每个对象维护一个引用计数器,当有一个地方引用它时,计数器加1;当引用失效时,计数器减1;任何时候计数器为0的对象即为可回收对象。但是这种方法存在一个缺陷,它不能解决循环引用的问题。

3.2 标记-清除算法

标记-清除算法是第一次用于商用计算机上,其基本思路是先扫描所有仍然有效的对象,标记它们,然后回收掉所有的未标记的对象,标记-清除算法效率较差,而且容易造成内存碎片。

3.3 标记-整理算法

标记-整理算法的目的是降低内存碎片化,其基本思路是首先扫描所有仍然有效的对象,标记它们,然后回收掉所有的未标记的对象,移动所有的可回收对象,完成之后才进行内存整理。

3.4 分代收集算法

分代收集算法是JVM内存管理的一个特色,其基本思路是把内存划分为几块,不同代的对象采用不同的算法进行回收。新生代采用复制算法,老年代采用标记-清除或标记-整理算法。新生代收集器是绝大部分对象的内存分配所在,所以占用空间小,回收频率低,每次GC仅回收少量对象。老年代对象一般较大,回收频率高,每次GC会回收大量对象,因此效率比新生代收集器高很多。

4.JVM性能分析工具

4.1 JConsole

JConsole是Oracle公司推出的图形化监视工具,可以直观地查看JVM的运行状态、配置信息、线程状况、类加载信息等。可以通过它查看内存占用情况、GC回收情况、线程活动、类加载等数据。安装JConsole后,在控制台输入jconsole命令即可打开JConsole。

4.2 VisualVM

VisualVM是Sun公司推出的一个基于图形界面的JVM监视工具。它内置了许多监测功能,包括CPU消耗、堆内存使用、GC操作、线程状况、类加载、JMX管理等,可以直观地查看JVM的运行状态。安装VisualVM后,在控制台输入visualvm命令即可打开VisualVM。

4.3 Flight Recorder

Flight Recorder是OpenJDK 7u40版本引入的新特性,它可以记录JVM运行时各种性能指标,包括CPU使用、内存使用、垃圾回收、锁操作等。通过它可以了解JVM在运行时的状态、瓶颈、热点代码、死锁等问题。Flight Recorder默认关闭,需要手动开启,可以在启动JVM时添加-XX:+UnlockCommercialFeatures -XX:+FlightRecorder开关。

5.内存管理优化技巧

5.1 JVM参数设置

JVM中有几个常用的参数影响JVM的性能:

  1. -Xms/-Xmx:设置初始最大堆内存,默认情况下JVM堆内存大小是物理内存的1/64。

  2. -Xmn:设置新生代大小,默认情况下新生代大小是整个堆内存的1/3。

  3. -Xss:设置每个线程的堆栈大小。

  4. -XX:NewRatio:设置新生代和老年代的比例,默认情况下,新生代是老年代的1倍。

  5. -XX:SurvivorRatio:设置新生代中Eden区与Survivor区的大小比例,默认情况下,Eden:Survivor=8:1。

  6. XX:+UseConcMarkSweepGC:设置CMS GC,Sun公司推出的一种GC算法。

  7. -XX:+UseParallelGC:设置Parallel Scavenge GC,IBM公司推出的一种GC算法。

  8. -XX:+PrintGCDetails:打印GC详细信息。

  9. -XX:+HeapDumpOnOutOfMemoryError:当JVM内存不足时,输出堆转储快照文件。

  10. -Dsun.zip.disableMemoryMapping:禁止内存映射,默认情况下JVM使用内存映射,即直接访问物理内存,提升随机读取性能。

5.2 HotSpot虚拟机参数设置

HotSpot虚拟机内部还有一些参数也影响JVM的性能,包括Java heap size(堆大小),Young Generation Size(年轻代大小),Tenured Generation Size(老年代大小),Thread Stack Size(线程堆栈大小)。可以通过启动JVM时增加-XX:+

  1. -XX:-TieredCompilation:关闭编译时优化,提升编译速度。

  2. -XX:+AlwaysPreTouch:程序启动时预先分配存活对象内存,降低Java堆碎片化。

  3. -XX:InitialRAMPercentage:指定JVM启动时占用的初始RAM百分比,默认是空闲内存的30%。

  4. -XX:MaxRAMPercentage:限制JVM占用的总RAM百分比。

  5. -XX:PermSize/-XX:MaxPermSize:限制Permanent generation的大小。

  6. -XX:MetaspaceSize/-XX:MaxMetaspaceSize:限制Metaspace的大小。

  7. -XX:+UseCompressedClassPointers:压缩类的指针,提升内存使用效率。

  8. -XX:+UseCompressedOops:压缩对象头,提升内存使用效率。

  9. -XX:+UseStringDeduplication:字符串重复利用,减少内存占用。

  10. -XX:+DisableExplicitGC:禁止显式GC,降低GC压力。

  11. -XX:ConcGCThreads:设置并发GC线程数。

5.3 类加载优化

类加载优化主要包括三个方面:

  1. 热点代码缓存:通过编译优化,将热点代码编译成本地代码,提升执行效率。

  2. 冷启动优化:通过预先加载常用类,减少冷启动时间。

  3. 反射优化:通过精心设计反射调用,减少反射调用带来的性能损耗。

5.4 安全管理器优化

安全管理器是Java提供的一套安全机制,用来控制程序对系统资源的访问权限。安全管理器可以防止恶意代码对系统的破坏性攻击。安全管理器的性能可以通过下列方式进行优化:

  1. 使用自定义安全策略:通过自定义安全策略,调整安全管理器对系统资源的访问权限。

  2. 暂时关闭安全管理器:暂时关闭安全管理器,降低安全管理器的性能损耗。

6.内存泄漏检测与处理

内存泄漏是指程序在运行过程中不断申请内存,但是却无法回收,最终导致系统资源紧张甚至导致系统崩溃的现象。通过内存泄漏检测,开发人员可以及时发现并解决内存泄漏问题。

  1. 哪些对象存在内存泄漏?

  2. 通过分析堆快照来定位内存泄漏对象。

  3. 通过内存泄漏工具来排查内存泄漏问题。

  4. 如何定位程序中的内存泄漏?

  5. 如何解决内存泄漏问题?

猜你喜欢

转载自blog.csdn.net/universsky2015/article/details/133385313
今日推荐