jvm调优参数

1.java进程命令行启动

java [-options] main_class_name [args...] 

2.java调优参数

  • JVM-X参数是非标准参数,在不同版本的jvm中,参数可能会有所不同,可以通过java -X查看非标准参数。

image

  • -XX参数也是非标准参数,主要用于JVM的调优和debug操作。java -XX:+PrintFlagsFinal -version可以查看所有的-XX选项, -XX使用有2种方式

    • 一种是boolean类型: 格式:-XX:+ | -XX:-
    • 一种是非boolean类型:格式:-XX:NewRatio=2
-Xmx30M  // 指定堆区最大内存
-Xms30M  // 指定堆区最小内存
-Xss128K  // 指定线程最大栈内存
-Xmn20M // 指定新生代的大小 一般设置为堆空间 1/3 或 1/4
-XX:MaxDirectoryMemorySize // 设置最大直接内存的大小,直接内存属于堆外空间,默认值为最大堆空间大小
-XX:SurvivorRatio=eden/from=eden/to // 设置新生代中 eden和from/to的空间比例 -XX:SurvivorRatio=5
-XX:NewRatio=老年代/新生代 // 设置新生代和老年代的比例
-XX:+PrintGC // 打印GC回收信息 堆前和堆后的相关内存信息会被打印
-XX:+PrintGCDetails // 打印GC回收的详细信息,显示每个内存区域的使用情况(eden,from,to 等区域)
-XX:+PrintHeapAtGC //  GC前和GC后打印堆中信息
-XX:+PrintGCTimeStamps // 打印GC发生的时间 配置 PrintGCDetail、PrintGc一块使用 输出虚拟机启动后的时间
-XX:+PrintGCApplicationConcurrentTime // 打印应用程序执行时间    
-XX:+PrintGCApplicationStoppedTime // 打印应用程序产生停顿的时间
-XX:+DoEscapeAnalysis // 开启逃逸分析
-server // 指定为server运行模式   
-XX:-UseTLAB // 不使用线程本地buffer
-XX:+EliminateAllocations // 开启标量替换,如果一个对象不会进行逃逸,那么可以尝试把一个对象分解成多个标量的形式,就可以不在堆上分配了,直接在栈上就能分配
-XX:PermSize30M // 指定永久区的的内存大小(JDK1.6、JDK1.7)堆内内存
-XX:MaxPermSize30M // 指定永久区最大的内存大小(jdk1.6、jdk1.7),默认为64MB
-XX:MaxMetaspaceSize // 指定元数据区的大小(jdk1.8),元数据区是用来存放类、字段等相关信息
-XX:+PrintReferenceGC // 打印JVM中的软引用、弱引用、虚引用和Finallize队列
-Xloggc:log/gc.log // 将gc相关日志输出到对应的文件中
-verbose:[class|gc|jni] // 打印类的加载,卸载、gc、jni相关信息
-XX:+TraceClassLoading // 只跟踪类的加载
-XX:+TraceClassUnLoading // 只跟踪类的卸载    
-XX:+PrintClassHistogram // 打印系统中类的分布情况
-XX:+PrintVMOptions // 打印虚拟机接收到的命令行显示参数
-XX:+PrintCommandLineFlags // 打印传递给虚拟机的显示和隐式参数
-XX:InitialHeapSize=10m // 指定初始化堆的大小
-XX:+PrintFlagsFinal // 打印系统中所有的参数值
-XX:+HeapDumpOnOutOfMemoryError // 开启内存溢出时导出堆信息
-XX:HeapDumpPath=xxx // 配合HeapDumpOnOutOfMemoryError使用,指定导出堆信息的文件路径
-XX:+UseSerialGC // 使用串行化垃圾收集器
-XX:MaxTenuringThreshold=xxx // 多少岁之后进入老年代,最大是15岁,默认也是15岁
-XX:PretenureSizeThreshold=xxx // 设置大于多少字节的对象直接进入老年代

3. 说明

  • -Xmx分配的最大内存跟实际可用的最大内存并不一致,会存在一些偏差,理论上说这些偏差等于from或者to的内存区域大小,但实际上JVM并没有直接使用from或者to计算,而是使用了某种算法进行了字节对齐

  • 如果堆中剩余内存不够分配,会扩展当前堆内存,前提是没有超过最大堆内存大小

  • 建议将新生代设置为堆空间的1/3或者1/4

  • NIO直接内存的访问速度远高于堆内存,申请空间的时间也远高于堆内存,直接内存适合申请次数少、访问较频繁的场合

  • JVM支持Client和Server两种运行模式,默认是Server模式,使用java -version可以查看到

  • 新生代适合复制算法,新生代中垃圾对象比较多,老年代适合标记压缩,老年代存活对象比较多

  • 老年代使用了一个卡表的数据结构(比特位集合)用来判断老年代对象是否持有新生代对象的引用,这样用来提高GC回收的速度,比特位集合为0时表示没有引用关系,直接跳过,反之需要进行扫描

  • 堆空间越大,一次 GC 时所需要的时间就越长,从而产生的停顿(STW)也越长

  • 软引用 、弱引用都非常适合来保存那些可有可无的缓存数据 如果这么做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出 而当内存资源充足时,这些缓存数据又可以存在相当长的时间,从而起到加速系统的作用

4.垃圾回收算法

  • 引用计数:解决不了循环引用问题
  • 标记压缩:GC时对存活对象镜像整理,都往一块连续空间移动
  • 标记清除:会产生大量碎片
  • 复制算法:浪费一半内存
  • 分代: 新生代、老年代
  • 分区:整个堆空间划分成连续的不同小区间,每一个小区间都独立使用,独立回收。

5.复制算法过程

在垃圾回收时,eden 空间中的存活对象会被复制到未使用的survivor 空间中(假设是to),正在使用的 survivor 空间(假设是from)中的年轻对象也会被复制到to空间中(大对象,或者老年对象会直接进入老年代,如果to空间已满,则对象也会直接进入老年代)。此时,eden空间和 fom 空间中的剩余对象就是垃圾对象,可以直接清空,to空间则存放此次回收后的存活对象。这种改进的复制算法,既保证了空间的连续性,又避免了大量的内存空间浪费。显示了复制算法的实际回收过程。当所有存活对象都复制到survivor区后,简单地清空 eden 区和备用的 survivor区即可。

image

对于新生代和老年代来说,通常,新生代回收的频率很高,但是每次回收的耗时都很短,而老年代回收的频率比较低,但是会消耗更多的时间。为了支持高频率的新生代回收,虚拟机可能使用一种叫作卡表(Card Table)的数据结构。卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代对象的引用。这样在新生代GC时可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有当卡表的标记位为1时,才需要扫描给定区域的老年代对象,而卡表位为0的所在区域的老年代对象,一定不含有新生代对象的引用。卡表中每一位表示老年代 4KB的空间,卡表记录为0的老年代区域没有任何对象指向新生代,只有卡表位为1的区域才有对象包含新生代引用,因此在新生代GC时,只需要扫描卡表位为1所在的老年代空间。使用这种方式,可以大大加快新生代的回收速度。

image

6.垃圾对象的定义

  • 对象的可触及性,即从根节点开始是否可以访问到这个对象,如果可以,则说明当前对象正在被使用,如果从所有的根节点都无法访问到某个对象,说明对象已经不再使用了,一般来说,此对象需要被回收。

  • 一个无法触及的对象有可能在某一个条件下复活自己,如果这样,那么对它的回收就是不合理的

为此,需要给出一个对象可触及性状态的定义,并规定在什么状态下,才可以安全地回收对象。

可触及性可以包含以下3种状态。

  1. 可触及的:从根节点开始,可以到达这个对象。。
  2. 可复活的: 对象的所有引用都被释放,但是对象有可能在finalize()函数中复活。
  3. 不可触及的: 对象的 finalize()函数被调用,并且没有复活,那么就会进入不可触及状态,不可触及的对象不可能被复活,因为 finalize()函数只会被调用一次。

以上3种状态中,只有在对象不可触及时才可以被回收。

7.引用和可触及性的强度

在Java中提供了4个级别的引用:

  1. 强引用
    • 强引用可以直接访问目标对象。
    • 强引用所指向的对象在任何时候都不会被系统回收,虚拟机宁愿抛出OOM 异常,也不会回收强引用指向对象。
    • 强引用可能导致内存泄漏,
  2. 软引用
    • 软引用是比强引用弱一点的引用类型。一个对象只持有软引用,那么当堆空间不足时,就会被回收。软引用使用java.lang.ref.SoftReference 类实现。
    • GC 未必会回收软引用的对象,但是,当内存资源紧张,软引用对象会被回收,所以软引用对象不会引起内存溢出
  3. 弱引用
    • 弱引用是一种比软引用较弱的引用类型。在系统GC时,只要发现弱引用,不管系统堆空间使用情况如何,都会将对象进行回收。
    • 由于垃圾回收器的线程通常优先级很低,因此并不一定能很快地发现持有弱引用的对象。在这种情况下,弱引用对象可以存在较长的时间。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册的引用队列中(这一点和软引用很像)。弱引用使用java.lang.ref.WeakReference 类实现。
  4. 虚引用。
    • 虚引用是所有引用类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。
    • 当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。
    • 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知应用程序对象的回收情况。
    • 由于虚引用可以跟踪对象的回收时间,因此,也可以将一些资源释放操作放置在虚引用中执行和记录。

除强引用外,其他3 种引用均可以在java.lang.ref包中找到它们的身影,其中 FinalReference 意味最终引用,它用以实现对象的 finalize()方法

image

强引用就是程序中一般使用的引用类型,强引用的对象是可触及的,不会被回收。相对的,软引用、弱引用和虚引用的对象是软可触及、弱可触及和虚可触及的,一 定条件下,都是可以被回收的

8.STW

垃圾回收器的任务是识别和回收垃圾对象进行内存清理。为了让垃圾回收器可以正常且高效地执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的是终止所有应用线程的执行,只有这样,系统中才不会有新的垃圾产生,同时停顿保证了系统状态在某一个瞬间的一致性,也有益于垃圾回收器更好地标记垃圾对象。因此,在垃圾回收时,都会产生应用程序的停顿。停顿产生时,整个应用程序会被卡死,没有任何响应,因此这个停顿也叫做Stop-The-World(STW)

posted on 2024-05-11 13:47  ccblblog  阅读(3)  评论(0编辑  收藏  举报

导航