当前位置: 首页 > news >正文

正邦高端网站建设重庆德旺广网络科技有限公司

正邦高端网站建设,重庆德旺广网络科技有限公司,有动效得网站,网站后台管理系统模板JVM 文章目录 JVM一.JVM结构1.1.JVM包含两个子系统和两个组件1.2.运行时数据区1.2.1.简介1.2.2.程序计数器1.2.3.虚拟机栈1.2.4.堆1.2.5.本地方法栈1.2.6.方法区(永久代实现)java8-1.2.7.元空间(Metaspace)1.2.8.JVM字节码执行引擎1.2.9.直接内存(Direct Memory)1.2.10.垃圾收集…JVM 文章目录 JVM一.JVM结构1.1.JVM包含两个子系统和两个组件1.2.运行时数据区1.2.1.简介1.2.2.程序计数器1.2.3.虚拟机栈1.2.4.堆1.2.5.本地方法栈1.2.6.方法区(永久代实现)java8-1.2.7.元空间(Metaspace)1.2.8.JVM字节码执行引擎1.2.9.直接内存(Direct Memory)1.2.10.垃圾收集系统 二.垃圾回收2.1.GC2.2.内存分配规则2.3.新生代,老年代,永久代,元空间2.3.1.分区2.3.2.比例2.3.3.原因 2.4.垃圾回收算法2.5.垃圾收集器2.6.判断对象是否可以被回收(标记算法)2.7.其他java内存溢出System.gc() 三.内存分配3.1.对象创建方式3.2.对象的分配3.3.对象的内存布局3.3.1.对象头3.3.1.1.对象标记(Mark Word)3.3.1.2.类元信息(Class pointer类型指针) 3.3.2.实例数据3.3.3.对齐填充 3.4. 对象内存查看3.5.类加载的机制及过程3.6.JVM加载Class文件的原理机制3.7.类加载器定义与分类3.8.自定义类加载器3.9.双亲委派模型:3.10.JVM新建对象3.11.Java引用类型 四.JVM调优4.1.工具4.2.调优参数4.3.性能调优4.4.程序算法改进程序逻辑算法提高性能 五.启动参数与命令5.1.设置参数方式5.2.java -help 标准参数(不会随着JDK 变化而变化版本的参数)5.3.java -X 非标准参数 (java -X命令能够获得当前JVM支持的所有非标准参数列表)5.4.java -XX 非固定参数5.5.其他命令 JVM (Java Virtual Machine) JAVA虚拟机. 由堆、栈、方法区所组成其中栈内存是给线程用的. 每个线程启动后虚拟机就会为其分配一块栈内存。 每个栈由多个栈帧(Frame)组成对应着每次方法调用时所占用的内存. 每个线程程只能有一个活动栈帧对应着当前正在执行的那个方法. 一.JVM结构 1.1.JVM包含两个子系统和两个组件 两个子系统为 类装载子系统 ClassLoader,执行引擎子系统 Execution engine; 类加载子系统:包含类加载器;根据给定的全限定类名装在class文件到运行时数据区的方法区;执行引擎:包含即时编译器(JITCompiler)和垃圾回收器(Garbage Collector);执行class文件中的命令; 两个组件为 运行时数据区 Runtime data Area,本地接口 Native Interface; 本地接口:与本地方法库交互,与其他变成语言交互的接口;运行时数据区域: 是jvm的内存;包含方法区,虚拟机栈,本地方法栈,堆,程序计数器; JAVA7: 1.2.运行时数据区 1.2.1.简介 程序计数器(Program Counter Register) 线程私有.当前线程所执行的字节码的行号指示器字节码解析器的工作是通过改变程序计数器的值来选取下一条需要执行的字节码指令分支、循环、跳转、异常处理、线程恢复等基础功能都依赖程序计数器来完成线程是不具备记忆功能,需要程序计数器. 本地方法栈(Native Method Stack) 线程私有.C所编写的Native方法相关.本地方法栈是为 虚拟机调用 Native本地方法 服务. Java虚拟机栈(Java Virtual Machine Stacks) 线程共享.虚拟机栈描述的是 Java方法执行 的内存模型每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储 局部变量表(基本类型对象引用和 returnAddress)、操作数栈、动态链接、方法出口等信息。每一个方法调用直至执行完的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 Java堆(Java Heap) 线程共享.虚拟机启动时创建.JVM 中内存最大的一块被所有线程共享java堆 唯一目的就是存放对象实例几乎’所有的对象实例都在这里分配内存;堆空间是垃圾收集器管理的主要区域. 方法区(Method Area)java8- 线程共享,非堆内存.用于存储已被虚拟机加载的类信息、常量、静态变量、JIT即时编译后的代码等数据。在Java8之后被更改为元数据空间.(JAVA7-)在方法区中有一个叫’运行时常量池’的区域主要用来存放编译器生成的各种字面量和符号引用在类加载完成后载入到运行时常量池中以便后续使用。JAVA7时已从方法区转移到堆内存,为了java8移除永久代做准备. 程序计数器,java虚拟机栈 为线程私有; 本地方法栈,Java堆,方法区 为线程共享; 1.2.2.程序计数器 程序计数器是一块较小的内存空间可以看作保存当前线程所正在执行的字节码指令的地址(行号)程序计数器线程私有, Java虚拟机的多线程是通过 线程轮流切换 并分配处理器执行时间的方式 来实现的同一时刻一个处理器都只会执行一条线程中的指令。 因此为了线程切换后能恢复到正确地执行位置每条线程都有一个独立的程序计数器各个线程之间计数器互不影响独立存储。 程序计数器内存区域是虚拟机中唯一没有规定 OutOfMemoryError 情况的区域。 1.2.3.虚拟机栈 Java虚拟机是线程私有的生命周期和线程相同。虚拟机栈描述的是Java方法执行的内存模型每个方法在执行的同时 都会创建一个栈帧 用于存储 局部变量表、操作数栈、动态链接、方法出口 等信息. java虚拟机栈的单位为栈帧: 局部变量表是用来存储临时8个基本数据类型、对象引用地址、returnAddress 类型。(returnAddress 中保存的是return后要执行的字节码的指令地址)操作数栈操作数栈就是用来操作的数据例如代码中有个 i 3*4他在一开始的时候就会进行操作读取我们的代码进行计算后再放入局部变量表中去动态链接方法中需要链接到别的方法中去(动态链接),存储链接的地方方法出口出口 正常就是return 不正常就是抛出异常 一个方法调用另一个方法会创建很多栈帧吗 如果一个栈中有动态链接调用别的方法就会去创建新的栈帧. 栈指向堆是什么意思 栈中要使用成员变量时,栈中不会存储成员变量只会存储一个应用地址 递归的调用自己会创建很多栈帧吗 递归的话也会创建多个栈帧就是在栈中一直从上往下排下去. 1.2.4.堆 java堆是java虚拟机所管理的内存中最大的一块是被所有线程共享的一块内存区域在虚拟机启动时创建。java堆目的就是存放对象实例。 所有的对象实例以及数组都要在堆上分配。 java堆是垃圾收集器管理的主要区域,从内存回收角度来看java堆可分为新生代和老年代。 从内存分配的角度看线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。 无论哪个区域存储的都是对象实例进一步地划分都是为了 更好地回收内存或者更快的分配内存。 根据Java虚拟机规范的规定java堆可以处于物理上不连续的内存空间中。 当前主流的虚拟机都是可扩展的(通过 -Xmx 和 -Xms 控制)。 如果堆中没有内存可以完成实例分配并且堆也无法再扩展时将会抛出 OutOfMemoryError 异常。 栈与堆的区别: 对比堆栈物理地址堆的物理地址分配对对象是不连续的。因此性能慢些。 在GC的时候也要考虑到不连续的分配所以有各种算法。栈使用的是数据结构中的栈先进后出的原则物理地址分配是连续的。所以性能快。内存分配堆因为是不连续的分配的内存是在运行期确认的因此大小不固定。一般堆远远大于栈。栈是连续的分配的内存大小要在编译期就确认大小是固定。存放内容堆存放 对象的实例和数组。更关注的是数据的存储栈存放 局部变量操作数栈返回结果。该区更关注的是程序方法的执行。可见度堆对于整个应用程序都是共享、可见的。栈只对于线程是可见的。线程私有。生命周期和线程相同。 1.2.5.本地方法栈 线程私有的. 用于执行本地方法这些方法是使用其他语言编写的并且与Java程序进行交互。本地方法栈中的帧用于保存本地方法的执行上下文和局部变量信息.本地方法栈提供了与本地库Native Library的连接使得Java程序能够调用本地库中的函数和方法。 1.2.6.方法区(永久代实现)java8- 方法区是所有线程共享的内存区域它用于存储已被Java虚拟机加载的 类信息、常量、静态变量、即时编译器编译后的代码 等数据。别命叫Non-Heap(非堆)。当方法区无法满足内存分配需求时抛出 OutOfMemoryError 异常。java8版本永久代已移除,使用元空间实现方法区 运行时常量池: 1.7- 运行时常量池在永久代中(HotSpot虚拟机对方法区的实现);1.7 常量池已经从永久代移动到堆内存中。仍然存在永久代但从这个版本开始JVM逐渐将永久代的功能移动到堆内存中包括运行时常量池的位置。1.8 常量池依旧在堆内存中.移除永久代,由元空间代替永久代(本地内存). 1.2.7.元空间(Metaspace) 在Java8开始取代了永久代的一种内存区域。与永久代不同元空间不使用Java虚拟机堆内存而是使用本地内存来存储类的元数据信息。 存储类的元数据元空间用于存储加载的类的元数据信息包括类的结构信息、字段信息、方法信息、注解、字节码等。这些元数据信息在程序运行时被JVM使用。动态大小调整与永久代不同元空间的大小不再受到默认固定大小的限制可以根据需要进行动态调整。元空间的大小受限于系统的可用本地内存大小可以通过设置JVM参数来限制元空间的最大大小。自动回收和垃圾回收由于元空间存储的是类的元数据信息而不是对象实例所以不再需要像永久代进行垃圾回收。元空间的自动回收主要发生在类加载和卸载过程中当某个类不再被引用或者无法被访问时相关的元数据将会被卸载。类型信息的存储方式元空间使用了一种新的机制来存储类的类型信息即虚拟机中的Class对象被替换为一种叫作Klass MetadataKlass元数据的结构。Klass元数据是在运行时根据类的加载和转换而动态生成的它包含了与类相关的信息并被存储在元空间中。元空间的内存管理元空间的内存管理由操作系统进行控制不再依赖于Java虚拟机的垃圾回收机制。元空间的分配和释放是基于本地内存的管理操作可通过操作系统提供的API进行管理。 1.2.8.JVM字节码执行引擎 执行引擎负责执行虚拟机的字节码一般先进行编译成机器码后执行。 “虚拟机”是一个相对于“物理机”的概念虚拟机的字节码是不能直接在物理机上运行的需要 JVM字节码执行引擎编译成机器码后才可在物理机上执行。 1.2.9.直接内存(Direct Memory) 直接内存是基于物理内存和Java虚拟机内存的中间内存,能在一些场景中显著提高性能。 直接内存不受Java堆大小限制它的分配和释放不依赖于JVM的垃圾回收机制而是通过操作系统提供的本地内存管理函数进行操作。 直接内存是通过操作系统的本地内存管理函数如malloc()、free()等来进行分配和释放的不需要经过JVM的对象分配和垃圾回收机制。 在JDK1.4中引入了NIO(New Input/Output)类一种基于通道(Chanel)与缓冲区(Buffer)的I/O方式NIO提供了一套非阻塞式的I/O操作方式使用直接内存可以提高I/O操作的效率和性能。 可以使用 Native函数库直接分配堆外内存然后通过一个存储在 Java 中的 DirectByteBuffer 对象作为对这块内存的引用进行操作。 1.2.10.垃圾收集系统 负责自动管理内存的组成部分。帮助Java程序管理内存对于垃圾对象的清除、存活对象的管理以及内存碎片的回收等工作都交由GC系统负责。 二.垃圾回收 GC发生在堆中,java语言最显著的特点就是引入了垃圾回收机制使java程序员在编写程序时 不再考虑内存管理的问题。 程序在运行过程中会产生大量的内存垃圾.为了确保程序运行时的性能java虚拟机在程序运行的过程中不断地进行自动的垃圾回收(GC)。 在java中不需要显式的去释放一个对象的内存的而是由虚拟机自行执行。 JVM中的垃圾回收线程是低优先级的在正常情况下是不会执行的只有在虚拟机空闲或者当前堆内存不足时才会触发执行 执行时扫描那些没有被任何引用的对象并将它们添加到要回收的集合中进行回收。 2.1.GC Minor GC(YoungGC) 清理整个新生代的过程edenS0\S1都会在空间不足时,触发minorGC的清理. 因为Java对象大多都是朝生夕死Minor GC非常频繁一般回收速度也非常快.Major GC(Full GC)老年代 区内存不足触发Major GC(Major GC通常是跟full GC是等价的). 出现了Major GC通常会伴随至少一次Minor GC。Major GC的速度通常会比Minor GC慢10倍以上。Mixed GC 混合GC,覆盖整个新生代空间及部分年老代空间的GC. 目前只有G1存在该行为其他收集器均不支持. full gc触发时机: 每次晋升到老年代的对象平均大小 老年代剩余空间MinorGC后存活的对象超过了老年代剩余空间(除CMS收集器)元空间空间不足执行System.gc()CMS标记清除收集器 GC异常堆内存分配很大的对象晋升失败promotion failed (年轻代晋升失败,比如eden区的存活对象晋升到幸存者区放不下又尝试直接晋升到老年区又放不下那么晋升失败,会触发 FullGC) 在发生Minor GC之前虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。 如果大于则进行 Minor GC如果小于则看 HandlePromotionFailure 设置是否允许担保失败(不允许则直接Full GC)。 如果允许担保失败继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小 如果大于则尝试 Minor GC(如果尝试失败也会触发Full GC)如果小于则进行 Full GC。 2.2.内存分配规则 对象优先在Eden区分配 多数情况对象都在新生代 Eden 区分配。当 Eden 区分配没有足够的空间进行 分配时虚拟机将会发起一次 Minor GC。 如果本次GC后还是没有足够的空间则将启用分配担保机制在老年代中分配内存。大对象直接进入老年代 -XX:PretenureSizeThreshold 大于此值得对象直接分配在老年代(只对Serial和ParNew两款收集器有效),以B为单位,1kb为1024 大对象是指需要大量连续内存空间的对象频繁出现大对象是致命的会导致在内存还有不少空间的情况下提前触发GC以获取足够的连续空间来安置新对象。 前面我们介绍过新生代使用的是标记-清除算法来处理垃圾回收的如果大对象直接在新生代分配就会导致Eden区和两个Survivor区之间发生大量的内存复制。 因此对于大对象都会直接在老年代进行分配。 (考虑ParNew加CMS的收集器组合)长期存活对象将进入老年代 数 -XX:MaxTenuringThreshold 晋升老年代阈值 虚拟机给每个对象定义了一个对象年龄的计数器如果对象在Eden区出生并且能够被Survivor容纳将被移动到Survivor空间中 这时设置对象年龄为1。对象在Survivor区中每熬过一次Minor GC年龄就加 1当年龄达到一定程度(默认 15) 就会被晋升到老年代。动态对象年龄判定 虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代.如果在Survivor空间中某年龄所有对象大小的总和大于Survivor空间的一半 年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到 MaxTenuringThreshold 中要求的年龄。空间分配担保 在发生Minor GC之前虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间如果老年代最大可用的连续空间大于新生代所有对象总空间那么Minor GC可以确保是安全的。 如果不成立则虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败。 如果允许那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小 如果大于将尝试着进行一次有风险的Minor GC.如果小于改为进行一次Full GC。 允许担保失败进行一次Full GC。 2.3.新生代,老年代,永久代,元空间 2.3.1.分区 在 Java 中堆被划分成两个不同的区域新生代 ( Young )、老年代 ( Old )。 新生代 ( Young )被划分为三个区域Eden、From Survivor、To Survivor。目的是为了使 JVM 能够 更好的管理堆内存中的对象包括内存的分配以及回收。 新生代中一般保存新出现的对象所以每次垃圾收集时都发现大批的对象死去只有少量的对象存活便采用了 复制算法 只需要付出少量存活对象的复制成本就可以完成收集。 老年代中一般保存存活了很久的对象他们存活率高、没有额外空间对它进行分配担保就必须采 用 “标记-清理”或者“标记-整理” 算法。 Java8- 永久代就是JVM的方法区。放着一些被虚拟机加载的类信息静态变量常量等数据。这个区中的东西比老年代和新生代更不容易回收。 Java8中已经移除了永久代新加了一个叫做空间的本地内存区. 2.3.2.比例 新生代:堆1/3 老年代:堆2/3 通过参数 –XX:NewRatio2来指定 eden:新生代8/10 survivor:新生代1/10 通过参数 –XX:SurvivorRatio8来设定 2.3.3.原因 为什么要这样分代? 其实主要原因就是可以根据各个年代的特点进行对象分区存储更便于回收采用最适当的收集算法 新生代中每次垃圾收集时都发现大批对象死去只有少量对象存活便采用了复制算法只需要付出少量存活对象的复制成本就可以完成收集。 老年代中因为对象存活率高、没有额外空间对它进行分配担保就必须采用“标记-清理”或者“标记-整理”算法。 新生代又分为Eden和Survivor (From与To)两个区。加上老年代就这三个区。 数据会首先分配到Eden区当中(特殊情况如果是大对象(大于PretenureSizeThreshold阈值)那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。 当Eden没有足够空间的时候就会触发jvm发起一次Minor GC。 如果对象经过一次Minor-GC还存活并且又能被Survivor空间接受那么将被移动到Survivor空间当中。并将其年龄设为1对象在Survivor每熬过一次Minor GC年龄就加1 当年龄达到一定的程度(默认15)时就会被晋升到老年代中了-XX:MaxTenuringThreshold15,设置晋升年龄. 为什么新生代要分Eden和两个 Survivor 区域 如果没有SurvivorEden区每进行一次Minor GC存活的对象就会被送到老年代。老年代很快被填满触发Major GC. 老年代的内存空间远大于新生代进行一次Full GC消耗的时间比Minor GC长得多,所以需要分为Eden和Survivor。Survivor的存在意义就是减少被送到老年代的对象进而减少Full GC的发生Survivor的预筛选保证只有经历15次Minor GC还能在新生代中存活的对象才会被送到老年代。设置两个Survivor区最大的好处就是解决了碎片化 刚刚新建的对象在Eden中经历一次MinorGCEden中的存活对象就会被移动到第一块survivor space S0Eden被清空 等Eden区再满了就再触发一次Minor GCEden和S0中的存活对象又会被复制送入第二块survivor spaceS1. (这种复制算法保证了S1中 来自S0和Eden两部分的存活对象 占用连续的内存空间避免了碎片化的发生) 元空间metaSpace替换永久代perm(方法区移至Metaspace字符串常量池移至Java Heap) 字符串常量池存在永久代中容易出现性能问题和内存溢出。类及方法的信息等比较难确定其大小因此对于永久代的大小指定比较困难太小容易出现永久代溢出太大则容易导致老年代溢出。永久代会为 GC 带来不必要的复杂度并且回收效率偏低。 2.4.垃圾回收算法 标记-清除算法标记无用对象然后进行清除回收。 a,标记(使用可达性分析算法;不使用引用计数法,存在循环引用); b,回收; 缺点效率低产生大量不连续的内存碎片提高了垃圾回收的频率。 复制算法按照容量划分两个大小相等的内存区域每次只使用其中一个区域.当一块用完的时候将活着的对象复制到另一块上然后再把已使用的内存空间一次清理掉。 缺点内存使用率不高只有原来的一半。对象存活率高时会频繁进行复制。实现:年轻代分为一个Eden和两个Survivor区,Eden与Survivor比例为 8:1:1,当其中eden和正在使用的survivor满时,发生gc,将存活对象复制到另一个幸存者区中. 标记-整理算法标记无用对象让所有存活的对象都向一端移动然后直接清除掉端边界以外的内存。 优点解决了标记-清理算法存在的内存碎片问题。缺点仍需要进行局部对象移动一定程度上降低了效率. 分代算法根据对象存活周期的不同将内存划分为几块一般是新生代和老年代新生代基本采用复制算法老年代采用标记整理算法。(永久代为方法区) 分代回收器有两个分区老年代和新生代新生代默认的空间占比总空间的 1/3老年代的默认占比是 2/3。 新生代使用的是复制算法新生代里有 3 个分区Eden、To Survivor、From Survivor它们的默认占比是 8:1:1. 执行流程如下 把Eden From Survivor 存活的对象放入 To Survivor 区清空 Eden 和 From Survivor 分区From Survivor 和 To Survivor 分区交换From Survivor 变 To SurvivorTo Survivor 变 From Survivor。每次在 From Survivor 到 To Survivor 移动时都存活的对象年龄就 1当年 龄到达15(默认配置)时升级为老生代。大对象也会直接进入老生代。老年代当空间占用到达某个值之后就会触发全局垃圾收回一般使用标记整理的执行算法。以上循环往复就构成了整个分代垃圾回收的整体执行流程。 2.5.垃圾收集器 垃圾收集器是垃圾回收算法(标记清除法、标记整理法、复制算法、分代算法)的具体实现不同垃圾收集器、不同版本的JVM所提供的垃圾收集器可能会有很在差别。 年轻代 Serial,Parallel Scavenge,PraNew老年代 Serial Old、Parallel Old、CMS堆(包括老年代和年轻代) G1 收集器分为分代收集器和分区收集器: 分代收集器Serial、ParNew、Parallel Scavenge、CMS、Serial Old、Parallel Old 分区收集器G1、ZGC(java11)、Shenandoah(java12) 收集器间搭配: Serial可搭配: Serial Old、CMS Parallel Scavenge可搭配: Serial Old、Parallel Old PraNew可搭配: Serial Old、CMS Serial 收集器(复制算法): 新生代单线程收集器.优点:简单高效. 适合单线程环境和对暂停时间要求不高的应用场景。 ParNew 收集器 (复制算法): 新生代收并行收集器实际上是Serial收集器的多线程版本在多核CPU环境下有着比Serial更好的表现.用于搭配CMS的新生代收集器. Parallel Scavenge 收集器 (复制算法): 新生代并行收集器追求高吞吐量高效利用CPU。 吞吐量 用户线程时间/(用户线程时间GC线程时间) 高吞吐量可以高效率的利用CPU时间尽快完成程序的运算任务适合后台应用等对交互响应 要求不高的场景. Serial Old 收集器 (标记-整理算法): 老年代单线程收集器Serial收集器的老年代版本 Parallel Old 收集器 (标记-整理算法) 老年代并行收集器吞吐量优先Parallel Scavenge收集器的老年代版本; CMS(Concurrent Mark Sweep 并发标记清除回收器)收集器 老年代并发收集器以获取最短回收停顿时间为目标的收集器具有高并发、低停顿的特点追求最短GC回收停顿时间。 以牺牲吞吐量为代价来获得 最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上非常适合。 在启动 JVM 的参数加上“-XX:UseConcMarkSweepGC”来指定使用CMS垃圾回收器。 CMS 使用的是标记-清除的算法实现的所以在gc的时候会产生大量的内存碎片当剩余内存不能满足程序运行要求时系统将会出现 Concurrent Mode Failure 临时CMS会采用 Serial Old 回收器进行垃圾清除(标记整理算法)此时的性能将会被降低。 CMS收集器有单独收集年老代空间的行为.(其他收集器发生老年代GC时,年轻代GC会一起发生) 回收过程: 初始标记仅标记GcRoot节点直接关联的对象该阶段速度会很快需在STW中进行。并发标记该阶段主要是做GC溯源工作GcTracing从根节点出发对整个堆空间进行可达性分析找出所有存活对象该阶段的GC线程会与用户线程同时执行。重新标记这个阶段主要是为了修正“并发标记”阶段由于用户线程执行造成的GC标记变动的那部分对象该阶段需要在STW中执行并且该阶段的停顿时间会比初始阶段要长不少。并发清除在该阶段主要是对存活对象之外的垃圾对象进行清除该阶段不需要停止用户线程是并发执行的。 G1(Garbage First)收集器 (标记-整理算法) Java堆 并发 分区回收 收集器G1收集器是JDK1.7提供的一个新收集器G1收集器基于“标记-整理”算法实现不会 产生内存碎片。 在不牺牲吞吐量前提下实现低停顿垃圾回收。 此外G1收集器回收的范围是整个Java堆(包括新生代老年代) 特性: 并发收集,与用户线程同时执行标记整理,不会产生内存碎片GC时停顿时间可控,尽可能会保证高吞吐量。对于堆的未使用内存可以返还给操作系统。JDK12 JAVA9时变为默认使用的收集器. 堆中的内存区域被划为了一个个Region区。Region区的默认数量限制为2048个.每个区大小为堆空间大小/2048.(不推荐用XX:G1HeapRegionSize指定) 每个分区都可能是年轻代也可能是老年代但是在同一时刻只能属于某个代。运行时每个分区都会被打上唯一的分区标识。 JVM不需要再为堆空间分配连续的内存堆空间可以是不连续物理内存来组成Region的集合. 有的区域垃圾对象少,有的垃圾对象多,G1优先回收垃圾对象多的区域. -XX:G1NewSizePercent 设置新生代初始占比(默认5) -XX:G1MaxNewSizePercent 设置新生代最大占比(默认60) 新生代中的Eden区和Survivor区对应的Region区比例默认8:1:1. G1中的年老代晋升条件和之前的相同达到年龄阈值的对象会被转入年老代的Region区中. 对于大对象的分配在G1中不会让大对象进入年老代在G1中由专门存放大对象的Region区叫做 Humongous 区 如果在分配对象时判定出一个对象属于大对象那么则会直接将其放入Humongous区存储。(超过单个普通Region区的50%为大对象单个Humongous区存不下时可能会横跨多个Region区存储) 可以避免一些生命周期短的大对象直接进入年老代节约年老代的内存空间可以有效避免年老代因空间不足时的GC开销。 FullGC时也会对Humongous区进行回收。 YoungGC: 在G1中当Eden域被用完时G1首先会计算回收当前的新生代空间需要花费的时间如果回收时间远远小于参数-XX:MaxGCPauseMills 值(默认200ms)那么不会触发YoungGC, 而是会继续为新生代增加新的Region区用于存放新分配的对象实例。 直至某次Eden区空间再次被放满并经过计算后此次回收的耗时接近-XX:MaxGCPauseMills参数设定的值才触发YoungGC。 YoungGC被触发时首先会将目标Region区中的存活对象移动(多线程并行复制)至幸存区空间(Survivor-from标签的区域).达到晋升年龄标准的对象也会被移入至年老代区中存储. G1内部做了优化一旦发现没有引用指向巨型对象则可直接在年轻代收集周期中被回收。 MixedGC: 当整个堆中年老代的区域占有率达到参数 -XX:InitiatingHeapOccupancyPercent(默认45) 设定的值后触发MixedGC. 触发时会回收所有新生代区和部分年老代区根据期望的GC停顿时间选择合适的年老代Region区优先回收以及大对象Humongous区. FullGC: 当 G1 无法在堆空间中申请新的分区时G1便会触发担保机制执行一次STW式单线程的 Full GCFull GC会对整堆做标记清除和压缩最后将只包含纯粹的存活对象。 从年轻代分区拷贝存活对象时无法找到可用的空闲分区从老年代分区转移存活对象时无法找到可用的空闲分区分配巨型对象时在老年代无法找到足够的连续分区 MixedGC 回收过程: 初始标记InitialMark先触发STW然后使用单条GC线程快速标记GCRoots直连的对象。并发标记ConcurrentMarking与CMS的并发标记过程一致(三色标记算法)采用多条GC线程与用户线程共同执行根据Root根节点标记所有对象。最终标记Remark同CMS的重新标记阶段主要是为了纠正并发标记阶段因用户操作导致的错标、误标、漏标对象。筛选回收Cleanup先对各个Region区的回收价值和成本进行排序找出「回收价值最大」的Region优先回收。 根据用户指定的期望停顿时间即-XX:MaxGCPauseMillis参数设定的值选择「价值最大且最符合用户预期」的Region区进行回收. 缺点: 停顿时间过短时,导致每次回收的空间只占堆内存的小部分.回收速度跟不上分配速度时导致垃圾堆积.相比CMS更高的内存开销和处理开销:需要维护额外的数据结构来管理分区和跟踪对象的存活情况.以及GC过程中的标记阶段、内存整理等操作.在小内存的应用中可能不如CMS 优点: 相比CMS采用的标记清除算法,G1的标记整理不会产生内存碎片.在不牺牲吞吐量前提下实现低停顿垃圾回收。(同时注重吞吐量和低延迟场景) Epsilon(JDK11): 用于测试的无操作收集器,装配该款GC收集器的JVM在运行期间不会发生任何GC相关的操作程序所分配的堆空间一旦用完Java程序就会因OOM原因退出。 ZGC(JDK11): ZGC主打的是超低延迟与吞吐量ZGC也会在尽可能堆吞吐量影响不大的前提下 实现在任意堆内存大小下都可以把垃圾回收的停顿时间限制在10ms以内的低延迟。没有实现分代架构. ZGC的目的主要有如下四点 奠定未来GC特性的基础。为了支持超大级别堆空间TB级别最高支持16TB。在最糟糕的情况下对吞吐量的影响也不会降低超过15%。GC触发产生的停顿时间不会偏差10ms。 ShenandoahGC(JDK12):追求极致低延迟.没有实现分代架构. ZGC是基于colored pointers染色指针实现的而ShenandoahGC是基于brooks pointers转发指针实现。 2.6.判断对象是否可以被回收(标记算法) 一般有两种方法来判断 引用计数器法为每个对象创建一个引用计数有对象引用时计数器1引用被释放时计数-1当计数器为0时就可以被回收。不能解决循环引用的问题!可达性分析算法从GC Roots开始向下搜索搜索所走过的路径称为引用链。涉及到的对象不能从GC Roots强引用可到达垃圾回收器都会进行清理来释放内存。 当一个对象到 GC Roots没有任何引用链相连时则证明此对象是可以被回收的。 GC Roots有: 类由系统类加载器加载的类。这些类从不会被卸载可以通过静态属性的方式持有对象的引用。一般情况下由自定义的类加载器加载的类不能成为GC Roots.线程存活的线程Java方法栈中的局部变量或者参数JNI方法栈中的局部变量或者参数JNI全局引用用做同步监控的对象被JVM持有的对象由于特殊的目的不被GC回收。可能是系统类加载器重要的异常处理类为处理异常预留的对象正在执行类加载的自定义的类加载器等. 2.7.其他 java内存溢出 Java存在着内存泄漏的情况导致内存泄露的原因长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露 尽管短生命周期对象已经不再需要但是因为长生命周期对象持有它的引用而导致不能被回收。 System.gc() public static void gc() {Runtime.getRuntime().gc(); }-XX: DisableExplicitGC 禁用gc()方法. ExplicitGCInvokesConcurrent 是G1垃圾回收器的一个JVM参数用于在执行显式垃圾回收时并发执行部分清理操作。 当设置为true时,当应用程序显式调用System.gc()方法或通过JMX接口执行显式的垃圾回收请求时 G1垃圾回收器将在执行垃圾回收的同时尽可能地启动并发标记和清理阶段。可以在显式垃圾回收请求期间减少停顿时间。 堆外内存常配合使用System GC 堆外内存主要针对java.nio.DirectByteBuffer这些对象的创建过程会通过Unsafe接口直接通过os::malloc来分配内存 然后将内存的起始地址和大小存到java.nio.DirectByteBuffer对象里这样就可以直接操作这些内存。 这些内存只有在DirectByteBuffer回收掉之后才有机会被回收因此如果这些对象大部分都移到了old区但是一直没有触发GC物理内存可能被他们耗尽. 因此为了避免这种悲剧的发生通过 -XX:MaxDirectMemorySize 来指定最大的堆外内存大小 当使用达到了阈值的时候将调用System.gc来做一次full gc以此来回收掉没有被使用的堆外内存. 显式调用System.gc垃圾回收并不能直接回收堆外内存而是通过垃圾回收器清理无法访问到的DirectByteBuffer对象并触发finalize()方法。 在finalize()方法中可以手动释放堆外内存的资源通常使用Unsafe接口的freeMemory()方法来释放内存。 三.内存分配 3.1.对象创建方式 new关键字 调用了构造函数Class的 newInstance方法 调用了构造函数Constructor类的 newInstance方法 调用了构造函数clone方法 没有调用构造函数反序列化 没有调用构造函数 3.2.对象的分配 对象优先在 Eden 区分配 当 Eden 区分配没有足够的空间进行分配时虚拟机将会发起一次 Minor GC。如果本次 GC 后还是没有足够的空间则将启用分配担保机制在老年代中分配内存。大对象直接进入老年代 需要大量连续内存空间的对象频繁出现大对象是致命的会导致在内存还有不少空间的情况下提前触发 GC 以获取足够的连续空间来安置新对象。 如果大对象直接在新生代分配就会导致 Eden 区和两个 Survivor 区之间发生大量的内存复制。因此对于大对象都会直接在老年代进行分配。长期存活对象将进入老年代 虚拟机采用分代收集的思想来管理内存内存回收时必须判断对象应该放在新生代或老年代。 虚拟机给每个对象定义了一个对象年龄的计数器如果对象在 Eden区出生并且能够被 Survivor 容纳将被移动到 Survivor 空间中这时设置对象年龄为 1。 对象在 Survivor 区中每「熬过」一次 Minor GC 年龄就加 1当年龄达到一定程度(默认 15) 就会被晋升到老年代.(-XX:MaxTenuringThreshold15,设置晋升年龄) 3.3.对象的内存布局 在 HotSpot 虚拟机里对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据和对齐填充. 3.3.1.对象头 3.3.1.1.对象标记(Mark Word) 用于存储对象自身的运行时数据如哈希码(hashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。 占用空间大小根据JVM决定为JVM的一个字大小也就是32位JVM中Mark Word占用4个字节64位JVM中占用8个字节。 默认存储对象的HashCode、分代年龄和锁标志位等信息。 这些信息都是与对象自身定义无关的数据所以MarkWord被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。 根据对象的状态复用自己的存储空间也就是说在运行期间MarkWord里存储的数据会随着’锁标志位’的变化而变化。 32位JVM 64位JVM Epoch时间戳用于记录偏向锁的撤销条件当其他线程尝试获取该对象的锁时需要检查该时间戳是否与对象头中的时间戳匹配。 如果不匹配则偏向锁会被撤销对象将升级为轻量级锁或重量级锁。 3.3.1.2.类元信息(Class pointer类型指针) 对象指向它的类元数据的指针虚拟机通过这个指针来确定这个对象是哪个类的实例。(4字节) 3.3.2.实例数据 存放类的属性(Field)数据信息包括父类的属性信息. 数组的实例部分还包括数组的长度. 这部分内存按4字节对齐。 3.3.3.对齐填充 虚拟机要求对象起始地址必须是8字节的整数倍。 填充数据不是必须存在的仅仅是为了字节对齐这部分内存按8字节补充对齐。 3.4. 对象内存查看 依赖: !--堆内存 存储结构-- dependencygroupIdorg.openjdk.jol/groupIdartifactIdjol-core/artifactIdversion0.16/version /dependency查看对象内存占用信息: System.out.println(ClassLayout.parseInstance(object).toPrintable()); //省略getter,setter,constructor public class ObjectHeadTest {static class LongObject {private Long num;}static class SimpleLongObject {private long num;}static class IntegerObject {private Integer num;}static class IntObject {private int num;}static class LongObjInObj {private LongObject num;}static class SimpleLongObjObjInObj {private SimpleLongObject num;}public static void main(String[] args) {LongObject longObject new LongObject(1L);System.out.println(longObject ClassLayout.parseInstance(longObject).toPrintable());LongObject nullLongObject new LongObject();System.out.println(nullLongObject ClassLayout.parseInstance(nullLongObject).toPrintable());SimpleLongObject simpleLongObject new SimpleLongObject(1L);System.out.println(simpleLongObject ClassLayout.parseInstance(simpleLongObject).toPrintable());SimpleLongObject nullSimpleLongObject new SimpleLongObject();System.out.println(nullSimpleLongObject ClassLayout.parseInstance(nullSimpleLongObject).toPrintable());IntegerObject integerObject new IntegerObject(1);System.out.println(integerObject ClassLayout.parseInstance(integerObject).toPrintable());IntObject intObject new IntObject(1);System.out.println(intObject ClassLayout.parseInstance(intObject).toPrintable());LongObjInObj longObjInObj new LongObjInObj(longObject);System.out.println(longObjInObj ClassLayout.parseInstance(longObjInObj).toPrintable());SimpleLongObjObjInObj simpleLongInObj new SimpleLongObjObjInObj(simpleLongObject);System.out.println(simpleLongInObj ClassLayout.parseInstance(simpleLongInObj).toPrintable());} }3.5.类加载的机制及过程 程序主动使用某个类时如果该类还未被加载到内存中则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。 加载 加载指的是将类的class文件读入到内存并将这些静态数据转换成方法区中的运行时数据结构 并在堆中生成一个代表这个类的java.lang.Class对象作为方法区类数据的访问入口这个过程需要类加载器参与。 类加载的最终产物就是位于堆中的 Class对象(不是目标类对象)该对象封装了类在方法区中的数据结构并且向用户提供了访问方法区数据结构的接口即Java反射的接口. Java类加载器由JVM提供是所有程序运行的基础JVM提供的这些类加载器通常被称为系统类加载器。 除此之外可以通过继承 ClassLoader基类 来创建自己的类加载器。 类加载器可以从不同来源加载类的二进制数据比如本地 Class文件、Jar包 Class文件、网络Class文件 等。连接过程 连接阶段负责把类的二进制数据合并到JRE中(意思就是将java类的二进制代码合并到JVM的运行状态之中)。 类连接可分为3个阶段: 验证确保加载的类信息符合JVM规范没有安全方面的问题。主要验证是否符合Class文件格式规范并且是否能被当前的虚拟机加载处理;准备正式为类变量(static变量)分配内存并设置类变量初始值的阶段这些内存都将在方法区中进行分配;解析虚拟机常量池的符号引用替换为字节引用过程; 初始化(初始化是为类的静态变量赋予正确的初始值) 初始化阶段是执行类构造器 () 方法的过程。 类构造器 ()方法是Java编译器生成的字节码中出现的一个特殊方法。负责执行类的静态变量初始化和静态代码块中的代码.代码从上往下执行。 当初始化一个类的时候如果发现其父类还没有进行过初始化则需要先触发其父类的初始化. 虚拟机会保证一个类的 () 方法在多线程环境中被正确加锁和同步. 3.6.JVM加载Class文件的原理机制 虚拟机把描述类的数据从Class文件加载到内存并对数据进行校验解析和初始化最终形成可以被虚拟机直接使用的java类型。 Java中的所有类都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类而它的工作就是把class文件从硬盘读取到内存中。 在写程序的时候几乎不需要关心类的加载因为这些都是隐式装载的除非有特殊的用法像是反射就需要显式的加载所需要的类。 类装载方式有两种 隐式装载程序在运行过程中当碰到通过new等方式生成对象时隐式调用类装载器加载对应的类到jvm中.显式装载通过class.forName() 等方法显式加载需要的类. 为了节省内存开销,Java类的加载是动态的并不会一次性将所有类全部加载后再运行而是保证程序运行的基础类(像是基类)完全加载到jvm中.其他类在需要的时候才加载。 3.7.类加载器定义与分类 实现通过类的全限定名获取类的二进制字节流的代码块叫做类加载器。 存在多种类加载器: 分工各自负责各自的区块为了实现委托模型 类加载器顺序: 启动类加载器(Bootstrap ClassLoader):用来加载java核心类库无法被java程序直接引用。扩展类加载器(extensions class loader):用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。系统类加载器(system class loader ):根据Java应用的类路径(CLASSPATH )来加载Java类。一般来说Java应用的类都是由它来完成加载的。 可以通过ClassLoader.getSystemClassLoader()获取。自定义类加载器: 通过继承java.lang.ClassLoader 类的方式实现。 类装载步骤 3.8.自定义类加载器 自定义类加载器的应用场景 加密Java代码可以轻易的被反编译如果你需要把自己的代码进行加密以防止反编译可以先将编译后的代码用某种加密算法加密 类加密后就不能再用Java的ClassLoader去加载类了这时就需要自定义ClassLoader在加载类的时候先解密类然后再加载。从非标准的来源加载代码如果字节码是放在数据库、甚至是在云端就可以自定义类加载器从指定的来源加载类。 综合运用比如应用需要通过网络来传输 Java 类的字节码为了安全性这些字节码经过了加密处理。 这个时候就需要自定义类加载器来从某个网络地址上读取加密后的字节代码接着进行解密和验证最后定义出在Java虚拟机中运行的类。 3.9.双亲委派模型: 双亲委派模型的工作过程 一个类加载器收到了类加载的请求不会先自己尝试去加载 这个类而是把这个请求委派给父类加载器去完成每一层的类加载器都是如此 这样所有的加载请求都会被传送到顶层的启动类加载器中只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时子加载器才会尝试去加载类。 //双亲委派模型的工作过程源码 protected synchronized Class? loadClass(String name, boolean resolve) throws ClassNotFoundException{// 检查类是否已存在Class c findLoadedClass(name);if (c null) {try {if (parent ! null) {c parent.loadClass(name, false);} else {c findBootstrapClassOrNull(name);}}catch (ClassNotFoundException e) {// 加载失败 抛出ClassNotFoundException thrown if class not found// from the non-null parent class loader//父类加载器无法完成类加载请求}if (c null) {// If still not found, then invoke findClass in order to find the class//子加载器进行类加载c findClass(name);}}if (resolve) {//判断是否需要链接过程参数传入resolveClass(c);}return c; }好处 安全性避免用户编写的类动态替换Java的核心类比如 String。避免了类的重复加载因为JVM中区分不同类不仅仅是根据类名相同的class文件被不同的ClassLoader加载就是不同的两个类。 3.10.JVM新建对象 User user new User();JVM做了哪些操作? 加载类信息:JVM将首先加载 User 类的字节码文件并解析其结构。这包括验证字节码的正确性并构建类的运行时数据结构.分配对象内存:JVM将根据 User 类的定义在堆上分配内存空间以创建一个新的对象。这个对象包含了类的实例变量和一些额外的管理信息。初始化对象:JVM会调用 User 类的构造函数来初始化这个对象。构造函数会为实例变量设置初始值执行其他必要的初始化代码。引用赋值:将对象的引用存储在 user 变量中使得可以通过该变量访问对象。 对象内存布局 3.11.Java引用类型 强引用 发生gc的时候不会被回收。软引用 SoftReference有用但不是必须的对象在发生内存溢出之前会被回收。弱引用 WeakReference有用但不是必须的对象在下一次GC时会被回收。虚引用 PhantomReference无法通过虚引用获得对象用 PhantomReference 实现虚引用虚引用的用途是在gc时返回一个通知。 四.JVM调优 4.1.工具 JDK 自带了很多监控工具都位于 JDK 的 bin 目录下其中最常用的是jconsole和jvisualvm这两款视图监控工具。 jconsole用于对JVM中的内存、线程和类等进行监控 jvisualvmJDK自带的全能分析工具可以分析内存快照、线程快照、程序 死锁、监控内存的变化、gc变化等。 4.2.调优参数 堆配置: -Xms2g初始化推大小为2g-Xmx2g堆最大内存为2g (为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间通常把最大、最小设置为相同的值)-XX:NewSizem;设置年轻代大小-XX:NewRatio4设置年轻代的和老年代的内存比例为 1:4 年轻代和年老代将根据默认的比例(12)分配堆内存.-XX:SurvivorRatio8设置新生代Eden和Survivor比例为 8:1 收集器配置: 串行收集器 -XX:UseSerialGC:设置串行收集器,只适用小数据量,一般不使用 并行收集器(吞吐量优先) -XX:UseParallelGC:设置并行收集器,年轻代-XX:ParallelGCThreadsn:设置并行收集器收集时使用的CPU数。并行收集线程数,此值最好配置与处理器数目相同。-XX:UseParalledlOldGC:设置并行年老代收集器;-XX:MaxGCPauseMillisn:设置年轻代并行收集最大的暂停时间(如果到这个时间了垃圾回收器依然没有回收完也会停止回收)-XX:UseAdaptiveSizePolicy:设置此选项以后并行收集器会自动选择年轻代区大小和相应的Survivor区比例以达到目标系统规定的最低响应时间或者收集频率等 此值建议使用并行收集器时一直打开并发收集器(响应时间优先) 减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等-XX:UseConcMarkSweepGC:设置并发收集器;指定使用 CMS Serial Old 垃圾回收器组合-XX:CMSFullGCsBeforeCompactionn:由于并发收集器不对内存空间进行压缩、整理、所以运行一段时间以后会产生“碎片” 使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理-XX:UseCMSCompactAtFullCollection打开对年老代的压缩。可能会影响性能但是可以消除碎片–XX:UseParNewGC指定使用 ParNew 垃圾回收器-XX:GCTimeRation:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1n)-XX:CMSIncrementalMode:设置为增量模式。适用于单CPU情况. 辅助的GC典型配置参数: -XX:PrintGC开启打印 gc 信息-XX:PrintGCDetails打印 gc 详细信息。-XX:PrintGCTimeStamps用于输出GC时间戳JVM启动到当前日期的总时长的时间戳形式 0.855: [GC (Allocation Failure) [PSYoungGen: 33280K-5118K(38400K)] 33280K-5663K(125952K), 0.0067629 secs] [Times: user0.01 sys0.01, real0.00 secs]-XX:PrintGCDateStamps 用于输出GC时间戳日期形式 2022-01-27T16:22:20.8850800: 0.299: [GC pause (G1 Evacuation Pause) (young), 0.0036685 secs]-XX:PrintHeapAtGC 在进行GC前后打印出堆的信息。-Xloggc:…/logs/gc.log:将日志输出到指定的文件中(已存在追加) 推荐配置 通过-XX:MaxRAMPercentage限制堆大小://参数需JDK 8u191、JDK 10及以上版本。 /使用容器内存。允许JVM从主机读取cgroup限制例如可用的CPU和RAM并进行相应的配置。当容器超过内存限制时会抛出OOM异常而不是强制关闭容器。 -XX:UseContainerSupport //设置JVM使用容器内存的初始百分比。建议与-XX:MaxRAMPercentage保持一致推荐设置为70.0。 -XX:InitialRAMPercentage70.0 //设置JVM使用容器内存的最大百分比。由于存在系统组件开销建议最大不超过75.0推荐设置为70.0。 -XX:MaxRAMPercentage70.0 //输出GC详细信息。 -XX:PrintGCDetails //输出GC时间戳。日期形式 -XX:PrintGCDateStamps //GC日志文件路径。需保证Log文件所在容器路径已存在. -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date %s).log //JVM发生OOM时自动生成DUMP文件。 -XX:HeapDumpOnOutOfMemoryError //DUMP文件路径。需保证DUMP文件所在容器路径已存在. -XX:HeapDumpPath/home/admin/nas/dump-${POD_IP}-$(date %s).hprof 通过-Xms -Xmx限制堆大小: 存在问题: 当规格大小调整后需要重新设置堆大小参数。当参数设置不合理时会出现应用堆大小未达到阈值但容器OOM被强制关闭的情况。 //设置JVM初始内存大小。建议与-Xmx相同避免每次垃圾回收完成后JVM重新分配内存。 推荐百分之70左右内存大小. -Xms2048m //设置JVM最大可用内存大小。为避免容器OOM请为系统预留足够的内存大小。 -Xmx2048m -XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:/home/admin/nas/gc-${POD_IP}-$(date %s).log -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/home/admin/nas/dump-${POD_IP}-$(date %s).hprof4.3.性能调优 线程池解决用户响应时间长的问题连接池JVM启动参数调整各代的内存比例和垃圾回收算法提高吞吐量 目标: GC的时间足够的小;GC的次数足够的少;发生Full GC的周期足够的长; 为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间通常把最大、最小设置为相同的值年轻代和年老代将根据默认的比例(12)分配堆内存更大的年轻代必然导致更小的年老代大的年轻代会延长普通GC的周期但会增加每次GC的时间小的年老代会导致更频繁的Full GC; 更小的年轻代必然导致更大年老代小的年轻代会导致普通GC很频繁但每次的GC时间会更短大的年老代会减少Full GC的频率;在配置较好的机器上(比如多核、大内存)可以为年老代选择并行收集算法 -XX:UseParallelOldGC 默认为Serial收集线程堆栈的设置每个线程默认会开启1M的堆栈用于存放栈帧、调用参数、局部变量等对大多数应用而言这个默认值太了 -Xss 一般256K就足用。理论上在内存不变的情况下减少每个线程的堆栈可以产生更多的线程但实际上还受限于操作系统。可以通过下面的参数打印Heap Dump信息 -XX:HeapDumpPath: 指定堆转储(JVM中对象的所有详细信息)文件的输出路径 -XX:PrintGCDetails 打印 gc 详细信息。 -XX:PrintGCTimeStamps 打印 gc 详细信息。 -Xloggc:/usr/aaa/dump/heap_trace.txt通过下面参数可以控制 OutOfMemoryError 时打印堆的信息 -XX:HeapDumpOnOutOfMemoryError请看一下一个时间的Java参数配置(服务器Linux 64Bit8Core×16G) JAVA_OPTS“$JAVA_OPTS -server -Xms3G -Xmx3G -Xss256k -XX:PermSize128m -XX:MaxPermSize128m -XX:UseParallelOldGC -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/usr/aaa/dump -XX:PrintGCDetails -XX:PrintGCTimeStamps -Xloggc:/usr/aaa/dump/heap_trace.txt -XX:NewSize1G -XX:MaxNewSize1G” 4.4.程序算法改进程序逻辑算法提高性能 五.启动参数与命令 5.1.设置参数方式 开发工具 IDEA 、Eclipse 在run configuration 里设置VM option运行jar包 java -XX:UseG1GC xxx.jar 线上环境: web容器Tomcat startup.sh - catalina.sh(卡特琳娜) 里设置JVM 参数jsp jinfo 查看某个java进程的参数然后再调整设置 真实调优: java -XX:UseG1GC xxx.jar 5.2.java -help 标准参数(不会随着JDK 变化而变化版本的参数) -d32 使用 32 位数据模型 (如果可用)-d64 使用 64 位数据模型 (如果可用)-server 选择 “server” VM ;默认 VM 是 server.-cp 目录和 zip/jar 文件的类搜索路径-classpath 目录和 zip/jar 文件的类搜索路径 用 ; 分隔的目录, JAR 档案 和 ZIP 档案列表, 用于搜索类文件。-D名称值 设置系统属性 可用System.getProperty(“property”)获取-verbose:[class|gc|jni] 启用详细输出-version 输出产品版本并退出-showversion 输出产品版本并继续-? -help 输出此帮助消息-X 输出非标准选项的帮助-enableassertions[:…|:]-ea[:…|:] 按指定的粒度启用断言-disableassertions[:…|:]-da[:…|:] 禁用具有指定粒度的断言-esa | -enablesystemassertions 启用系统断言-dsa | -disablesystemassertions 禁用系统断言-agentlib:[选项] 加载本机代理库 , 例如 -agentlib:hprof 另请参阅 -agentlib:jdwphelp 和 -agentlib:hprofhelp-agentpath:[选项] 按完整路径名加载本机代理库-javaagent:[选项] 加载 Java 编程语言代理, 请参阅 java.lang.instrument-splash: 使用指定的图像显示启动屏幕 5.3.java -X 非标准参数 (java -X命令能够获得当前JVM支持的所有非标准参数列表) -Xmixed 混合模式执行 (默认)-Xint 仅解释模式执行-Xbootclasspath: 用 ; 分隔的目录和 zip/jar 文件 设置搜索路径以引导类和资源-Xbootclasspath/a: 用 ; 分隔的目录和 zip/jar 文件 附加在引导类路径末尾-Xbootclasspath/p: 用 ; 分隔的目录和 zip/jar 文件 置于引导类路径之前-Xdiag 显示附加诊断消息-Xnoclassgc 禁用类垃圾收集-Xincgc 启用增量垃圾收集-Xloggc: 将 GC 状态记录在文件中 (带时间戳)-Xbatch 禁用后台编译-Xprof 输出 cpu 配置文件数据-Xfuture 启用最严格的检查, 预期将来的默认值-Xrs 减少 Java/VM 对操作系统信号的使用-Xcheck:jni 对 JNI 函数执行其他检查-Xshare:off 不尝试使用共享类数据-Xshare:auto 在可能的情况下使用共享类数据 (默认)-Xshare:on 要求使用共享类数据, 否则将失败。-XshowSettings 显示所有设置并继续-XshowSettings:all 显示所有设置并继续-XshowSettings:vm 显示所有与 vm 相关的设置并继续-XshowSettings:properties 显示所有属性设置并继续-XshowSettings:locale 显示所有与区域设置相关的设置并继续 5.4.java -XX 非固定参数 使用方式: -XX: 启用选项-XX:- 不启用选项-XX: 给选项设置一个数字类型值可跟单位例如 32k, 1024m, 2g-XX: 给选项设置一个字符串值例如-XX:HeapDumpPath./dump.core 行为参数(功能开关): -XX:-UseSerialGC 启用串行GC-XX:-UseParallelGC 启用并行GC-XX:GCTimeRatio99 设置用户执行时间占总时间的比例(默认值99即1%的时间用于GC)-XX:MaxGCPauseMillistime 设置GC的最大停顿时间(只对Parallel Scavenge有效)-XX:UseParNewGC 使用ParNewSerial Old收集器组合-XX:ParallelGCThreads 设置执行内存回收的线程数在 UseParNewGC 的情况下使用-XX:-UseParallelOldGC 对Full GC启用并行当-XX:-UseParallelGC 启用时该项自动启用,使用Parallel Scavenge Parallel Old组合收集器-XX:-UseConcMarkSweepGC 对老生代采用标记清除交换算法进行GC CMS(Concurrent Mark Sweep)收集器(标记-清除算法)-XX:ScavengeBeforeFullGC 新生代GC优先于Full GC执行-XX:-DisableExplicitGC 禁止调用System.gc()但jvm的gc仍然有效-XX:MaxFDLimit 最大化文件描述符的数量限制-XX:UseGCOverheadLimit 在抛出OOM之前限制jvm耗费在GC上的时间比例-XX:UseThreadPriorities 启用本地线程优先级-XX:AutoBoxCacheMax 缓存最大值,默认为127 (Integer默认缓存 -128~127) 性能调优: -Xms 设置初始 Java 堆大小-Xmx 设置最大 Java 堆大小-Xss 设置 Java 线程堆栈大小,默认1m-XX:PretenureSizeThreshold 大于此值得对象直接分配在老年代(只对Serial和ParNew两款收集器有效),以B为单位,1kb学制为1024-XX:NewSize2.125m 新生代对象生成时占用内存的默认值-XX:MaxNewSizesize 新生成对象能占用内存的最大值-XX:PermSize64m 方法区分配的初始内存-XX:MaxPermSize64m 方法区能(永久代)占用内存的最大值-XX:NewRatio2 新生代内存容量与老生代内存容量的比例,默认2,即 1:2-XX:SurvivorRatio8 Eden区域Survivor区的容量比值如默认值为8代表EdenSurvivor1Survivor28:1:1-XX:MaxTenuringThreshold15 对象在新生代存活区切换的次数(坚持过MinorGC的次数每坚持过一次该值就增加1),大于该值会进入老年代(年龄阈值)-XX:MinHeapFreeRatio40 GC后java堆中空闲量占的最小比例-XX:MaxHeapFreeRatio70 GC后java堆中空闲量占的最大比例-XX:ThreadStackSize512 设置线程栈大小若为0则使用系统默认值-XX:MetaspaceSize128m 元空间(永久代) 初始大小;元空间的默认初始大小是20.75MB-XX:MaxMetaspaceSize128m 元空间(永久代) 最大空间 一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值-XX:LargePageSizeInBytes4m 设置用于Java堆的大页面尺寸-XX:ReservedCodeCacheSize32m 保留代码占用的内存容量-XX:UseLargePages 使用大页面内存 调试参数: -XX:-CITime 打印消耗在JIT编译的时间-XX:ErrorFile./hs_err_pid.log 保存错误日志或者数据到文件中-XX:-ExtendedDTraceProbes 开启solaris特有的dtrace探针-XX:HeapDumpPath./java_pid.hprof 指定导出堆信息时的路径或文件名-XX:-HeapDumpOnOutOfMemoryError 当首次遭遇OOM时导出此时堆中相关信息-XX:OnError“;” 出现致命ERROR之后运行自定义命令-XX:OnOutOfMemoryError“;” 当首次遭遇OOM时执行自定义命令-XX:-PrintClassHistogram 遇到Ctrl-Break后打印类实例的柱状信息与jmap -histo功能相同-XX:-PrintConcurrentLocks 遇到Ctrl-Break后打印并发锁的相关信息与jstack -l功能相同-XX:-PrintCommandLineFlags 打印在命令行标记,用于查看jvm参数-XX:-PrintCompilation 当一个方法被编译时打印相关信息-XX:-PrintGC 每次GC时打印相关信息-XX:-PrintGC Details 每次GC时打印详细信息-XX:-PrintGCTimeStamps 打印每次GC的时间戳-XX:-TraceClassLoading 跟踪类的加载信息-XX:-TraceClassLoadingPreorder 跟踪被引用到的所有类的加载信息-XX:-TraceClassResolution 跟踪常量池-XX:-TraceClassUnloading 跟踪类的卸载信息-XX:-TraceLoaderConstraints 跟踪类加载器约束的相关信息 5.5.其他命令 JPS 查看java进程idjinfo [options] -flags显示 JVM 启动时设置的标志Flag信息。-sysprops显示 Java 系统属性System Property信息。-commandline显示 Java 进程的启动命令行参数信息。-flag 显示指定 Flag 的设置值。-flag [/-]将指定 Flag 的设置值在运行时开启或关闭。-help帮助信息。 jstat 查看性能 类加载、内存、垃圾收集情况、 JIT 实时编译的运行时数据 jstat [-t] [-h] jstat [option [interval [s|m] [count] ] ] option参数解释-class显示ClassLoad的相关信息-compiler显示JIT编译的相关信息-gc显示和gc相关的堆信息--gccapacity显示各个代的容量以及使用情况-gccause显示垃圾回收的相关信息(通-gcutil)同时显示最后一次或当前正在发生的垃圾回收的诱因-gcnew显示新生代的信息-gcnewcapacity显示新生代大小和使用情况-gcold显示老年代和永久代的信息-gcoldcapacity显示老年代的大小-gcpermcapacity显示永久代的大小-gcutil显示垃圾收集信息-printcompilation输出JIT编译的方法信息 参数解释-t可以在打印的列上加上Timestamp列用于显示系统运行的时间-h可以在周期性数据的时候可以在指定输出多少行以后输出一次表头interval执行每次的间隔时间单位为毫秒count用于指定输出多少次记录缺省则会一直打印
http://www.dnsts.com.cn/news/151170.html

相关文章:

  • 武功网站开发非自己的网站如何做二次跳转
  • 有哪些可以在网上做兼职的网站江苏品牌网站建设
  • 精品资源共享课网站建设 碧辉腾乐上海设计公司 快消品
  • 帮别人做彩票网站小企业网站建设怎样
  • 深圳杰恩创意设计有限公司网站互联网产品推广是做什么的
  • 鲜花网站建设策划方案酒店管理专业建设规划
  • 工艺品网站设计html5软件下载官网
  • 网站短信接口怎么做网站建设一龙条
  • 怎么做盗版电影网站吗商丘建设网站
  • 网站所需的主要功能wordpress多个视频
  • 网站设建设廊坊seo按天计费
  • 优化网站平台揭阳网站推广教程
  • flash网站模板中心十大免费ppt网站在线
  • 企业需求做网站在哪儿交易网站地图做关键词排名
  • 长沙服装网站建设外贸出口剪标尾单
  • icp备案网站名称seo建站需求
  • 佛山网站制作网址高端网站建设哪个好
  • 红旗网站建设国外网站建立
  • 郑州做网站九零后wordpress的文档主题
  • 邯郸网站设计西安大公司
  • 怎么搭建一个网站品牌注册需要什么条件
  • 网站首页菜单栏表怎么做网站建设活动方案
  • 做网站首页尺寸大小wordpress内容管理系统
  • 网站建设 会议纪要如何把音乐导入wordpress
  • 微信公众号怎么做链接网站吗网销外包
  • 商丘做网站的费用企业网站开发需要多少钱
  • 网络营销推广的三板斧优化设计卷子答案
  • 咸宁做网站的公司那家便宜wordpress发布文章后页面错误
  • 做的网站在百度找不到了illustrator
  • 大丰住房和城乡建设局网站wordpress去掉浏览数