1. JVM内存结构
JVM内存结构可以分为三大部分,分别是:
1.1 堆内存
堆是JVM中最大的一块内存,用于存储对象实例,几乎所有的对象实例都分配在堆中。当JVM启动时,会自动分配一个初始大小的堆空间,可通过-Xms参数来设置初始堆大小,同时JVM还会根据应用程序运行情况动态调整堆大小,但是最大堆大小不会超过-Xmx参数设置。
//设置初始堆大小为512M,最大堆大小为1G
java -Xms512m -Xmx1g
1.2 栈内存
栈内存用于存储局部变量、方法参数、操作数栈、方法调用栈等信息。每个线程都会有一个私有的栈空间,栈内存大小由-Xss参数来设置,如果线程申请的栈空间超过了-Xss设置的大小,就会抛出StackOverflowError。
//设置每个线程的栈大小为256k
java -Xss256k
1.3 方法区
方法区(也称为永久代)用于存储类信息、常量、静态变量、即时编译器编译后的代码等信息。方法区一般不受-Xmx参数的限制,它的大小由PermSize和MaxPermSize参数控制。在JDK8及以后的版本中,永久代已被移除,取而代之的是元空间。
//设置永久代初始大小为64M,最大为256M
java -XX:PermSize=64m -XX:MaxPermSize=256m
2. 内存回收
Java内存的回收主要是针对堆内存而言的。通常情况下,Java内存已经被使用的空间无法再继续使用,需要回收这些垃圾空间。Java虚拟机会定期运行垃圾回收器来回收堆内存中不再使用的对象空间。
2.1 垃圾回收算法
JVM中主要有标记-清除算法、复制算法、标记-整理算法三种垃圾回收算法。
2.1.1 标记-清除算法
标记-清除算法分为标记和清除两个阶段。在标记阶段,Java虚拟机会标记所有存活的对象,然后在清除阶段回收不再使用的对象空间。
缺点:标记-清除算法会产生内存碎片,当内存空间出现碎片时,如果需要申请较大的内存空间,就可能无法找到连续的内存块,导致垃圾回收失败。
2.1.2 复制算法
复制算法将堆内存分为两个大小相等的空间,每次只使用其中一个空间。当这个空间填满之后,就将其中存活的对象复制到另一个空间,然后将这个空间清空。
优点:复制算法能够避免内存碎片问题,而且每次清理的空间较小,垃圾回收时间较短。
缺点:由于需要将存活的对象复制到另一个空间,在对象存活率较高的情况下,需要复制的次数也会比较多,复制算法的效率可能不如其他算法。
2.1.3 标记-整理算法
标记-整理算法分为标记和整理两个阶段。在标记阶段,Java虚拟机会标记所有存活的对象,然后将它们压缩到内存的一端。在整理阶段,Java虚拟机会将没有使用的空间回收。
优点:标记-整理算法能够避免内存碎片问题,而且回收的效率比标记-清除算法高。
缺点:标记-整理算法需要进行压缩操作,如果存活对象比较多,那么压缩的时间可能会比较长。
2.2 垃圾回收器
Java虚拟机提供了多种垃圾回收器,每个垃圾回收器都有自己的特点,可以根据实际应用场景来选择。
2.2.1 Serial垃圾回收器
Serial垃圾回收器是一种单线程的垃圾回收器,它使用复制算法进行垃圾回收,适用于内存比较小的应用场景。在JDK9及以后的版本中,Serial垃圾回收器已被移除。
//使用Serial垃圾回收器
java -XX:+UseSerialGC
2.2.2 Parallel垃圾回收器
Parallel垃圾回收器是一种多线程的垃圾回收器,它使用复制算法进行垃圾回收,适用于多核CPU的应用场景。
//使用Parallel垃圾回收器
java -XX:+UseParallelGC
2.2.3 CMS垃圾回收器
CMS(Concurrent Mark Sweep)垃圾回收器是一种并发的垃圾回收器,它使用标记-清除算法进行垃圾回收,但是它会在标记阶段和清除阶段尽量减少停顿时间。CMS垃圾回收器适用于响应时间要求较高的应用场景。
//使用CMS垃圾回收器
java -XX:+UseConcMarkSweepGC
2.2.4 G1垃圾回收器
G1(Garbage First)垃圾回收器是一种面向服务端应用的垃圾回收器,它使用了全新的标记-整理算法,能够在尽可能短的时间内回收大量不再使用的内存空间。
//使用G1垃圾回收器
java -XX:+UseG1GC
3. 内存调优
内存调优是指优化Java应用程序的内存使用情况,目的是尽量缩小内存的使用,降低垃圾回收的次数和时间,从而提高应用程序的性能。
3.1 根据实际需要调整堆大小
通过调整-Xms和-Xmx参数,可以根据实际需要来设置堆的大小。
注意:不要将-Xms参数设置得太低,否则会导致频繁的垃圾回收,而不要将-Xmx参数设置得太高,否则会导致运行时内存不足。
3.2 避免大量的对象创建和使用
在Java程序中,创建对象是一种较为耗费内存的行为,因此应该尽量避免大量的对象创建和使用。可以使用对象池、缓存等技术来避免对象的重复创建和销毁。
3.3 尽量使用基本类型而非封装类型
封装类型(如Integer、Long等)是一种较为消耗内存的类型,因此在代码编写过程中应该尽量使用基本类型(如int、long等)。
3.4 合理使用垃圾回收器和垃圾回收策略
应该根据应用程序的实际情况来选择垃圾回收器和垃圾回收策略,以达到最优的垃圾回收效果。
4. 总结
JVM内存空间是Java语言的核心之一,了解JVM内存空间结构、内存回收、垃圾回收器以及内存调优等知识,对于优化Java应用程序的性能具有非常重要的意义。