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

崇左北京网站建设如何搭建网络论坛平台

崇左北京网站建设,如何搭建网络论坛平台,有限责任公司注册要求,app应用公司JVM 内存模型概述 堆 堆是JVM内存中最大的一块内存空间#xff0c;该内存被所有线程共享#xff0c;几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代#xff0c;新生代又被进一步划分为Eden和Survivor区#xff0c;最后Survivor由From Survivor和To Su…JVM 内存模型概述 堆 堆是JVM内存中最大的一块内存空间该内存被所有线程共享几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代新生代又被进一步划分为Eden和Survivor区最后Survivor由From Survivor和To Survivor组成。 在Java6版本中永久代在非堆内存区到了Java7版本永久代的静态变量和运行时常量池被合并到了堆中而到了Java8永久代被元空间取代了。 结构如下图所示 程序计数器Program Counter Register 程序计数器是一块很小的内存空间主要用来记录各个线程执行的字节码的地址例如分支、循环、跳转、异常、线程恢复等都依赖于计数器。 由于Java是多线程语言当执行的线程数量超过CPU核数时线程之间会根据时间片轮询争夺CPU资源。如果一个线程的时间片用完了或者是其它原因导致这个线程的CPU资源被提前抢夺那么这个退出的线程就需要单独的一个程序计数器来记录下一条运行的指令。 方法区Method Area 方法区主要是用来存放已被虚拟机加载的类相关信息包括类信息、运行时常量池、字符串常量池。 类信息又包括了类的版本、字段、方法、接口和父类等信息。 JVM在执行某个类的时候必须经过加载、链接包括验证、准备、解析三个阶段、初始化。在加载类的时候JVM会先加载class文件而在class文件中除了有类的版本、字段、方法和接口等描述信息外还有一项信息是常量池(Constant Pool Table)用于存放编译期间生成的各种字面量和符号引用。 字面量包括字符串常量String a“b”、声明为final的属性、以及一些基本类型的属性。符号引用则包括类和方法的全限定名例如String这个类它的全限定名就是Java/lang/String、类引用、方法引用以及成员变量引用例如String str“abc”其中str就是成员变量引用等。 而当类加载到内存中后JVM就会将class文件常量池中的内容存放到运行时的常量池中在解析阶段JVM会把符号引用替换为直接引用对象的索引值。 链接阶段 验证阶段的主要目的是对字节码字节流进行校验判断其内容是否符合当前虚拟机的规范以确保被加载的代码运行后不会对虚拟机造成损害。准备阶段主要是用于对类或接口中的 “静态变量” 分配内存空间以及对变量设置默认的初始值。对于final static修饰的变量直接赋值为用户的定义值。例如private final static int value123会在准备阶段分配内存并初始化值为123而如果是 private static int value123这个阶段value的值仍然为0。解析将符号引用转为直接引用的过程。我们知道在编译时Java类并不知道所引用的类的实际地址因此只能使用符号引用来代替。类结构文件的常量池中存储了符号引用包括类和接口的全限定名、类引用、方法引用以及成员变量引用等。如果要使用这些类和方法就需要把它们转化为JVM可以直接获取的内存地址或指针即直接引用。 初始化阶段为类的变量进行赋值。 例如类中的一个字符串常量在class文件中时存放在class文件常量池中的在JVM加载完类之后JVM会将这个字符串常量放到运行时常量池中并在解析阶段指定该字符串对象的索引值。运行时常量池是全局共享的多个类共用一个运行时常量池class文件中常量池多个相同的字符串在运行时常量池只会存在一份。 知识小提示 Class.forName() 加载初始化ClassLoader.loadClass()只加载并不初始化。 方法区与堆空间类似也是一个共享内存区所以方法区是线程共享的。假如两个线程都试图访问方法区中的同一个类信息而这个类还没有装入JVM那么此时就只允许一个线程去加载它另一个线程必须等待。 在HotSpot Java8虚拟机静态变量和运行时常量池转移到了堆中方法区并用元空间class metadata存储类的元数据。 虚拟机栈VM stack Java虚拟机栈是线程私有的内存空间它和Java线程一起创建。当创建一个线程时会在虚拟机栈中申请一个线程栈用来保存方法的局部变量、操作数栈、动态链接方法和返回地址等信息并参与方法的调用和返回。每一个方法的调用都伴随着栈帧的入栈操作方法的返回则是栈帧的出栈操作。 本地方法栈Native Method Stack 本地方法栈跟Java虚拟机栈的功能类似Java虚拟机栈用于管理Java函数的调用而本地方法栈则用于管理本地方法的调用。但本地方法并不是用Java实现的而是由C语言实现的。 JVM即时编译器JIT Java 类初始化完成后类在调用执行过程中执行引擎会把字节码转为机器码然后在操作系统中才能执行。 字节码转成机器码再执行。 最初虚拟机中的字节码是由解释器 Interpreter 完成编译的当虚拟机发现某个方法或代码块的运行特别频繁的时候就会把这些代码认定为“热点代码”。 为了提高热点代码的执行效率在运行时即时编译器JIT会把这些代码编译成与本地平台相关的机器码并进行各层次的优化然后保存到内存中。在发生调用时会直接执行缓存的机器语言。 热点代码直接执行机器码。 编译优化技术-方法内联 方法内联的优化行为就是把目标方法的代码复制到发起调用的方法之中避免发生真实的方法调用。 方法调用在执行前保护现场并记忆执行的地址执行后要恢复现场并按原来保存的地址继续执行。 因此方法调用会产生一定的时间和空间方面的开销。 JVM会自动识别热点方法并对它们使用方法内联进行优化。但要强调一点热点方法不一定会被JVM做内联优化如果这个方法体太大了JVM将不执行内联操作。而方法体的大小阈值我们也可以通过参数设置来优化 默认情况下方法体大小小于325字节的都会进行内联我们可以通过-XX:MaxFreqInlineSizeN来设置大小值不经常执行的方法默认情况下方法大小小于35字节才会进行内联我们也可以通过-XX:MaxInlineSizeN来重置大小值。 热点方法的优化可以有效提高系统性能一般我们可以通过以下几种方式来提高方法内联 通过设置JVM参数来减小热点阈值或增加方法体阈值以便更多的方法可以进行内联但这种方法意味着需要占用更多地内存在编程中避免在一个方法中写大量代码习惯使用小方法体尽量使用final、private、static关键字修饰方法编码方法因为继承会需要额外的类型检查。 编译优化技术-逃逸分析 逃逸分析Escape Analysis是判断一个对象是否被外部方法引用或外部线程访问的分析技术编译器会根据逃逸分析的结果对代码进行优化。 栈上分配 在HotSpot中暂时没有实现这项优化。 在Java中默认创建一个对象是在堆中分配内存的而当堆内存中的对象不再使用时则需要通过垃圾回收机制回收这个过程相对分配在栈中的对象的创建和销毁来说更消耗时间和性能。这个时候逃逸分析如果发现一个对象只在方法中使用就会将对象分配在栈上。 锁消除 StringBuffer中的append方法被Synchronized关键字修饰会使用到锁会导致性能下降。 但实际上在以下代码测试中StringBuffer和StringBuilder的性能基本没什么区别。这是因为在局部方法中创建的对象只能被当前线程访问无法被其它线程访问这个变量的读写肯定不会有竞争这个时候JIT编译会对这个对象的方法锁进行锁消除。 public static String getString(String s1, String s2) {StringBuffer sb new StringBuffer();sb.append(s1);sb.append(s2);return sb.toString();}标量替换 逃逸分析证明一个对象不会被外部访问如果这个对象可以被拆分的话当程序真正执行的时候可能不创建这个对象而直接创建它的成员变量来代替。将对象拆分后可以分配对象的成员变量在栈或寄存器上原本的对象就无需分配内存空间了。这种编译优化就叫做标量替换。 例如下面的代码 public void foo() {TestInfo info new TestInfo();info.id 1;info.count 99;...//to do something}逃逸分析后代码会被优化为 public void foo() {id 1;count 99;...//to do something}可以通过设置JVM参数来开关逃逸分析、单独开关锁消除和标量替换 -XX:DoEscapeAnalysis # 开启逃逸分析jdk1.8默认开启其它版本未测试 -XX:-DoEscapeAnalysis # 关闭逃逸分析-XX:EliminateLocks # 开启锁消除jdk1.8默认开启其它版本未测试 -XX:-EliminateLocks # 关闭锁消除-XX:EliminateAllocations # 开启标量替换jdk1.8默认开启其它版本未测试 -XX:-EliminateAllocations # 关闭标量替换JVM 垃圾回收机制 垃圾回收算法 JDK1.7 update14 之后Hotspot虚拟机所有的回收器整理如下以下为服务端垃圾收集器 可以通过 jmap -heap pid 看到垃圾收集器类型。 [localhost /] $jmap -heap 1131 Attaching to process ID 1131, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.412-b0-internalusing thread-local object allocation. Garbage-First (G1) GC with 4 thread(s)Heap Configuration:MinHeapFreeRatio 40MaxHeapFreeRatio 70MaxHeapSize 8589934592 (8192.0MB)NewSize 1363144 (1.2999954223632812MB)MaxNewSize 5152702464 (4914.0MB)OldSize 5452592 (5.1999969482421875MB)NewRatio 2SurvivorRatio 8MetaspaceSize 536870912 (512.0MB)CompressedClassSpaceSize 1073741824 (1024.0MB)MaxMetaspaceSize 2147483648 (2048.0MB)G1HeapRegionSize 2097152 (2.0MB)Heap Usage: G1 Heap:regions 4096capacity 8589934592 (8192.0MB)used 1019280184 (972.0613327026367MB)free 7570654408 (7219.938667297363MB)11.865983065217733% used G1 Young Generation: Eden Space:regions 228capacity 3372220416 (3216.0MB)used 478150656 (456.0MB)free 2894069760 (2760.0MB)14.17910447761194% used Survivor Space:regions 5capacity 10485760 (10.0MB)used 10485760 (10.0MB)free 0 (0.0MB)100.0% used G1 Old Generation:regions 257capacity 1986002944 (1894.0MB)used 530643768 (506.0613327026367MB)free 1455359176 (1387.9386672973633MB)26.719183352831926% used39609 interned Strings occupying 4011032 bytes.GC性能衡量指标 吞吐量这里的吞吐量是指应用程序所花费的时间和系统总运行时间的比值。我们可以按照这个公式来计算GC的吞吐量系统总运行时间应用程序耗时GC耗时。如果系统运行了100分钟GC耗时1分钟则系统吞吐量为99%。GC的吞吐量一般不能低于95%。停顿时间指垃圾收集器正在运行时应用程序的暂停时间。对于串行回收器而言停顿时间可能会比较长而使用并发回收器由于垃圾收集器和应用程序交替运行程序的停顿时间就会变短但其效率很可能不如独占垃圾收集器系统的吞吐量也很可能会降低。垃圾回收频率多久发生一次指垃圾回收呢通常垃圾回收的频率越低越好增大堆内存空间可以有效降低垃圾回收发生的频率但同时也意味着堆积的回收对象越多最终也会增加回收时的停顿时间。所以我们只要适当地增大堆内存空间保证正常的垃圾回收频率即可。 查看分析GC日志 我们需要通过JVM参数预先设置GC日志通常有以下几种JVM参数设置 -XX:PrintGC 输出GC日志 -XX:PrintGCDetails 输出GC的详细日志 -XX:PrintGCTimeStamps 输出GC的时间戳以基准时间的形式 -XX:PrintGCDateStamps 输出GC的时间戳以日期的形式如 2013-05-04T21:53:59.2340800 -XX:PrintHeapAtGC 在进行GC的前后打印出堆的信息 -Xloggc:../logs/gc.log 日志文件的输出路径一般是选择 -XX:PrintGCTimeStamps -XX:PrintGCDetails -Xloggc:/log/heapTest.log 这样的形式输出gc日志。 我们可以通过GCViewer工具打开日志文件图形化界面查看整体的GC性能。 另外GCeasy是一款非常直观的GC日志分析工具我们可以将日志文件压缩之后上传到GCeasy官网即可看到非常清楚的GC日志分析结果。 GC调优策略 降低Minor GC频率 通常情况下由于新生代空间较小Eden区很快被填满就会导致频繁Minor GC因此我们可以通过适当增大新生代空间来降低Minor GC的频率。 但是如果在堆内存中存在较多的长期存活的对象此时增加年轻代空间虽然降低了Minor GC的频率但是会增加Minor GC的时间。 如果堆中的短期对象很多那么扩容新生代单次Minor GC时间不会显著增加。因此单次Minor GC时间更多取决于GC后存活对象的数量而非Eden区的大小。 降低Full GC的频率 减少创建大对象可以降低Full GC的频率。 例如我之前碰到过一个一次性查询出60个字段的业务操作这种大对象如果超过年轻代最大对象阈值会被直接创建在老年代即使被创建在了年轻代由于年轻代的内存空间有限通过Minor GC之后也会进入到老年代。这种大对象很容易产生较多的Full GC。 我们可以将这种大对象拆解出来首次只查询一些比较重要的字段如果还需要其它字段辅助查看再通过第二次查询显示剩余的字段。 可以通过参数-XX:PetenureSizeThreshold设置直接被分配到老年代的最大对象这时如果分配的对象超过了设置的阀值对象就会直接被分配到老年代这样做的好处就是可以减少新生代的垃圾回收。 增大堆内存空间在堆内存不足的情况下增大堆内存空间且设置初始化堆内存为最大堆内存也可以降低Full GC的频率。 选择合适的GC回收器 当我们对停顿时间有要求一般会选择响应速度较快的GC回收器CMSConcurrent Mark Sweep回收器和G1回收器都是不错的选择。当我们的对系统吞吐量有要求时就可以选择Parallel Scavenge回收器来提高系统的吞吐量。 优化JVM内存分配 JDK1.8是默认开启-XX:UseAdaptiveSizePolicy配置项的它表示JVM将会动态调整Java堆中各个区域的大小以及进入老年代的年龄–XX:NewRatio和-XX:SurvivorRatio将会失效。 在JDK1.8中不要随便关闭UseAdaptiveSizePolicy配置项除非你已经对初始化堆内存/最大堆内存、年轻代/老年代以及Eden区/Survivor区有非常明确的规划了。JVM将会分配最小堆内存年轻代和老年代按照默认比例1:2进行分配年轻代中的Eden和Survivor则按照默认比例8:2进行分配。 现模拟一个接口假设需要满足一个5W的并发请求且每次请求会产生20KB对象我们可以通过千级并发创建一个1MB对象的接口来模拟万级并发请求产生大量对象的场景具体代码如下 RequestMapping(value /test1)public String test1(HttpServletRequest request) {ListByte[] temp new ArrayListByte[]();Byte[] b new Byte[1024*1024];temp.add(b);return success;}使用 ‘apache-bench’ 对系统进行压测。 可以看到当并发数量到了一定值时吞吐量就上不去了响应时间也迅速增加。 -XX:PrintGCTimeStamps -XX:PrintGCDetails -Xloggc:/log/heapTest.log 增加jvm参数输出gc日志并通过GCViewer工具打开它。 主页面显示FullGC发生了13次右下角显示年轻代和老年代的内存使用率几乎达到了100%。而FullGC会导致stop-the-world的发生从而严重影响到应用服务的性能。此时我们需要调整堆内存的大小来减少FullGC的发生。 参考指标 GC频率高频的FullGC会给系统带来非常大的性能消耗虽然MinorGC相对FullGC来说好了许多但过多的MinorGC仍会给系统带来压力。内存这里的内存指的是堆内存大小堆内存又分为年轻代内存和老年代内存。首先我们要分析堆内存大小是否合适其实是分析年轻代和老年代的比例是否合适。如果内存不足或分配不均匀会增加FullGC严重的将导致CPU持续爆满影响系统性能。吞吐量频繁的FullGC将会引起线程的上下文切换增加系统的性能开销从而影响每次处理的线程请求最终导致系统的吞吐量下降。延时JVM的GC持续时间也会影响到每次请求的响应时间。 具体调优方法 调整堆内存空间减少FullGC通过日志分析堆内存基本被用完了而且存在大量FullGC这意味着我们的堆内存严重不足这个时候我们需要调大堆内存空间。 java -jar -Xms4g -Xmx4g heapTest-0.0.1-SNAPSHOT.jar -Xms堆初始大小-Xmx堆最大值调大堆内存之后我们再来测试下性能情况发现吞吐量提高了40%左右响应时间也降低了将近50%。 再查看GC日志发现FullGC频率降低了老年代的使用率只有16%了。 调整年轻代减少MinorGC通过调整堆内存大小我们已经提升了整体的吞吐量降低了响应时间。那还有优化空间吗我们还可以将年轻代设置得大一些从而减少一些MinorGC。 java -jar -Xms4g -Xmx4g -Xmn3g heapTest-0.0.1-SNAPSHOT.jar再进行压测发现吞吐量上去了。 再查看GC日志发现MinorGC也明显降低了GC花费的总时间也减少了。 设置Eden、Survivor区比例在JVM中如果开启 AdaptiveSizePolicy则每次 GC 后都会重新计算 Eden、From Survivor和 To Survivor区的大小计算依据是 GC 过程中统计的 GC 时间、吞吐量、内存占用量。这个时候SurvivorRatio默认设置的比例会失效。 在JDK1.8中默认是开启AdaptiveSizePolicy的我们可以通过-XX:-UseAdaptiveSizePolicy关闭该项配置或显示运行-XX:SurvivorRatio8将Eden、Survivor的比例设置为8:2。大部分新对象都是在Eden区创建的我们可以固定Eden区的占用比例来调优JVM的内存分配性能。 再进行性能测试我们可以看到吞吐量提升了响应时间降低了。 内存持续上升如何排查 常量的监控和内存诊断工具 top命令 top命令是我们在Linux下最常用的命令之一它可以实时显示正在执行进程的CPU使用率、内存使用率以及系统负载等信息。其中上半部分显示的是系统的统计信息下半部分显示的是进程的使用率统计信息。 还可以通过top -Hp pid查看具体线程使用系统资源情况 vmstat命令 vmstat是一款指定采样周期和次数的功能性监测工具不仅可以统计内存的使用情况还可以观测到CPU的使用率、swap的使用情况。它经常被用来观察进程的上下文切换。 [localhost /] $vmstat 1 3 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----r b swpd free buff cache si so bi bo in cs us sy id wa st0 0 0 873972 0 44484 0 0 13769 161061 0 1 0 0 99 0 00 0 0 874104 0 44484 0 0 292 21792 370586 343134 0 0 100 0 00 0 0 874108 0 44484 0 0 948 42760 452392 298924 0 0 100 0 0r等待运行的进程数b处于非中断睡眠状态的进程数swpd虚拟内存使用情况free空闲的内存buff用来作为缓冲的内存数si从磁盘交换到内存的交换页数量so从内存交换到磁盘的交换页数量bi发送到块设备的块数bo从块设备接收到的块数in每秒中断数cs每秒上下文切换次数us用户CPU使用时间sy内核CPU系统使用时间id空闲时间wa等待I/O时间st运行虚拟机窃取的时间。 pidstat 命令 pidstat是Sysstat中的一个组件也是一款功能强大的性能监测工具可以通过命令yum install sysstat安装该监控组件。之前的top和vmstat两个命令都是监测进程的内存、CPU以及I/O使用情况而pidstat命令则是深入到线程级别。 [localhost /] $sudo yum install sysstat Loaded plugins: branch, fastestmirror, langpacks alios.7u2.base.x86_64 | 2.4 kB 00:00:00 ops.7.noarch | 2.4 kB 00:00:00 ops.7.x86_64 | 2.4 kB 00:00:00 taobao.7.noarch.stable | 2.3 kB 00:00:00 taobao.7.x86_64.stable | 2.4 kB 00:00:00 (1/2): alios.7u2.base.x86_64/x86_64/primary_db | 7.8 MB 00:00:00 (2/2): ops.7.x86_64/7/x86_64/primary_db | 1.8 MB 00:00:00 Loading mirror speeds from cached hostfile Resolving Dependencies -- Running transaction check --- Package sysstat.x86_64 0:10.1.5-7.1.alios7 will be updated --- Package sysstat.x86_64 0:10.1.5-18.1.alios7 will be an update -- Finished Dependency Resolution ...Updated:sysstat.x86_64 0:10.1.5-18.1.alios7Complete!通过pidstat -help命令我们可以查看到有以下几个常用的参数来监测线程的性能 [localhost /] $pidstat help Usage: pidstat [ options ] [ interval [ count ] ] Options are: [ -d ] [ -h ] [ -I ] [ -l ] [ -r ] [ -s ] [ -t ] [ -U [ username ] ] [ -u ] [ -V ] [ -w ] [ -C command ] [ -p { pid [,...] | SELF | ALL } ] [ -T { TASK | CHILD | ALL } ]常用参数 -u默认的参数显示各个进程的cpu使用情况-r显示各个进程的内存使用情况-d显示各个进程的I/O使用情况-w显示每个进程的上下文切换情况-p指定进程号-t显示进程中线程的统计信息。 我们可以通过相关命令例如ps或jps查询到相关进程ID再运行以下命令来监测该进程的内存使用情况其中pidstat的参数-p用于指定进程ID-r表示监控内存的使用情况1表示每秒的意思3则表示采样次数。 [localhost /] $jps 35877 Jps 1149 jar[localhost /] $pidstat -p 1149 -r 1 3 Linux 4.19.91-009.ali4000.x86_64 12/16/2024 _x86_64_ (104 CPU)05:15:12 PM UID PID minflt/s majflt/s VSZ RSS %MEM Command 05:15:13 PM 2347 1149 5226.00 0.00 26185424 11287740 89.71 java 05:15:14 PM 2347 1149 52.00 0.00 26185424 11287740 89.71 java 05:15:15 PM 2347 1149 80.00 0.00 26185424 11287740 89.71 java Average: 2347 1149 1786.00 0.00 26185424 11287740 89.71 java其中显示的几个关键指标的含义是 Minflt/s任务每秒发生的次要错误不需要从磁盘中加载页Majflt/s任务每秒发生的主要错误需要从磁盘中加载页VSZ虚拟地址大小虚拟内存使用KBRSS常驻集合大小非交换区内存使用KB。 如果我们需要继续查看该进程下的线程内存使用率则在后面添加-t指令即可 [localhost /] $pidstat -p 1149 -r -t 1 3 Linux 4.19.91-009.x86_64 12/16/2024 _x86_64_ (104 CPU)05:35:51 PM UID TGID TID minflt/s majflt/s VSZ RSS %MEM Command 05:35:52 PM 2347 1149 - 33.00 0.00 26185424 11289492 89.72 java 05:35:52 PM 2347 - 1149 0.00 0.00 26185424 11289492 89.72 |__java 05:35:52 PM 2347 - 1152 0.00 0.00 26185424 11289492 89.72 |__java 05:35:52 PM 2347 - 1154 0.00 0.00 26185424 11289492 89.72 |__Gang worker#0 ( 05:35:52 PM 2347 - 1155 0.00 0.00 26185424 11289492 89.72 |__Gang worker#1 ( 05:35:52 PM 2347 - 1156 0.00 0.00 26185424 11289492 89.72 |__Gang worker#2 ( 05:35:52 PM 2347 - 1157 0.00 0.00 26185424 11289492 89.72 |__Gang worker#3 ( 05:35:52 PM 2347 - 1158 0.00 0.00 26185424 11289492 89.72 |__G1 Concurrent R 05:35:52 PM 2347 - 1159 0.00 0.00 26185424 11289492 89.72 |__G1 Concurrent R 05:35:52 PM 2347 - 1160 0.00 0.00 26185424 11289492 89.72 |__G1 Concurrent R 05:35:52 PM 2347 - 1161 0.00 0.00 26185424 11289492 89.72 |__G1 Concurrent R 05:35:52 PM 2347 - 1162 0.00 0.00 26185424 11289492 89.72 |__G1 Concurrent R 05:35:52 PM 2347 - 1164 0.00 0.00 26185424 11289492 89.72 |__G1 Main Concurr 05:35:52 PM 2347 - 1165 0.00 0.00 26185424 11289492 89.72 |__Gang worker#0 ( 05:35:52 PM 2347 - 1166 2.00 0.00 26185424 11289492 89.72 |__VM Thread 05:35:52 PM 2347 - 1167 0.00 0.00 26185424 11289492 89.72 |__Reference Handl 05:35:52 PM 2347 - 1168 0.00 0.00 26185424 11289492 89.72 |__Finalizer ......jstat命令 jstat可以监测Java应用程序的实时运行情况包括堆内存信息以及垃圾回收信息。通过jstat -options查看jstat有哪些操作 [localhost /] $jstat -options -class # 显示ClassLoad的相关信息 -compiler # 显示JIT编译的相关信息 -gc # 显示和gc相关的堆信息 -gccapacity # 显示各个代的容量以及使用情况 -gccause # 显示垃圾回收的相关信息通-gcutil同时显示最后一次或当前正在发生的垃圾回收的诱因 -gcmetacapacity # 显示Metaspace的大小 -gcnew # 显示新生代信息 -gcnewcapacity # 显示新生代大小和使用情况 -gcold # 显示老年代信息 -gcoldcapacity # 显示老年代的大小 -gcutil # 显示垃圾收集信息 -printcompilation # 输出JIT编译的方法信息jstat的功能比较多在这里我例举一个常用功能如何使用jstat查看堆内存的使用情况。我们可以用jstat -gc pid查看 [localhost /] $jstat -gc 1149S0C S1C S0U S1U EC EU OC OU MC MU YGC YGCT FGC FGCT GCT0.0 12288.0 0.0 12288.0 6152192.0 3325952.0 3620864.0 980306.8 144512.0 139704.4 2703 400.717 0 0.000 400.717S0C年轻代中To Survivor的容量单位KBS1C年轻代中From Survivor的容量单位KBS0U年轻代中To Survivor目前已使用空间单位KBS1U年轻代中From Survivor目前已使用空间单位KBEC年轻代中Eden的容量单位KBEU年轻代中Eden目前已使用空间单位KBOCOld代的容量单位KBOUOld代目前已使用空间单位KBMCMetaspace的容量单位KBMUMetaspace目前已使用空间单位KBYGC从应用程序启动到采样时年轻代中gc次数YGCT从应用程序启动到采样时年轻代中gc所用时间(s)FGC从应用程序启动到采样时old代全gcgc次数FGCT从应用程序启动到采样时old代全gcgc所用时间(s)GCT从应用程序启动到采样时gc用的总时间(s)。 jstack命令 jstack是一种线程堆栈分析工具最常用的功能就是使用 jstack pid 命令查看线程的堆栈信息通常会结合top -Hp pid 或 pidstat -p pid -t一起查看具体线程的状态也经常用来排查一些死锁的异常。 [adminnui-sus033004190120.pre.na610 /] $jstack 1149 2024-12-16 18:36:24 Full thread dump OpenJDK 64-Bit Server VM (25.412-b0-internal mixed mode):http-nio-9666-exec-27 #35606 daemon prio5 os_prio0 tid0x00007fda90395000 nid0x31a24 waiting on condition [0x00007fd9bdffc000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park0(Native Method)- parking to wait for 0x0000000544ef1d08 (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at sun.misc.Unsafe.park(Unsafe.java:1038)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:216)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2087)at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:471)at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:90)at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:33)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:879)http-nio-9666-exec-26 #35605 daemon prio5 os_prio0 tid0x00007fda90394000 nid0x31a23 waiting on condition [0x00007fd9be0fd000]java.lang.Thread.State: TIMED_WAITING (parking)at sun.misc.Unsafe.park0(Native Method)- parking to wait for 0x0000000544ef1d08 (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at sun.misc.Unsafe.park(Unsafe.java:1038)at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:216)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2087)at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:471)at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:90)at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:33)at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)at java.lang.Thread.run(Thread.java:879)....... 每个线程堆栈的信息中都可以查看到线程ID、线程的状态wait、sleep、running 等状态以及是否持有锁等。 nid0x31a24 为线程ID的16进制值java.lang.Thread.State 为线程状态 jmap命令 我们可以用jmap -heap pid来查看堆内存初始化配置信息以及堆内存的使用情况 [localhost /] $jmap -heap 1149 Attaching to process ID 1149, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.412-b0-internalusing thread-local object allocation. Garbage-First (G1) GC with 4 thread(s)Heap Configuration:MinHeapFreeRatio 40MaxHeapFreeRatio 70MaxHeapSize 10737418240 (10240.0MB)NewSize 1363144 (1.2999954223632812MB)MaxNewSize 6442450944 (6144.0MB)OldSize 5452592 (5.1999969482421875MB)NewRatio 2SurvivorRatio 8MetaspaceSize 536870912 (512.0MB)CompressedClassSpaceSize 1073741824 (1024.0MB)MaxMetaspaceSize 2147483648 (2048.0MB)G1HeapRegionSize 2097152 (2.0MB)Heap Usage: G1 Heap:regions 5120capacity 10737418240 (10240.0MB)used 6167643792 (5881.923477172852MB)free 4569774448 (4358.076522827148MB)57.44065895676613% used G1 Young Generation: Eden Space:regions 2349capacity 6299844608 (6008.0MB)used 4926210048 (4698.0MB)free 1373634560 (1310.0MB)78.19573901464713% used Survivor Space:regions 6capacity 12582912 (12.0MB)used 12582912 (12.0MB)free 0 (0.0MB)100.0% used G1 Old Generation:regions 814capacity 3707764736 (3536.0MB)used 1226753680 (1169.9234771728516MB)free 2481011056 (2366.0765228271484MB)33.08607118701503% used48518 interned Strings occupying 4906176 bytes.我们还可以使用jmap -histo[:live] pid查看堆内存中的对象数目、大小统计直方图如果带上live则只统计活对象 [adminnui-sus033004190120.pre.na610 /] $jmap -histo:live 1149num #instances #bytes class name ----------------------------------------------2: 2939598 94067136 java.util.HashMap$Node3: 3396231 81509544 java.lang.String4: 34451 27505272 [Ljava.util.HashMap$Node;5: 262144 27262976 org.apache.logging.log4j.core.async.RingBufferLogEvent6: 14796 20654880 [I7: 262144 6291456 org.apache.logging.log4j.core.time.MutableInstant8: 40716 6123728 [Ljava.lang.Object;9: 11845 5259976 [B10: 98521 3152672 java.util.concurrent.ConcurrentHashMap$Node11: 25094 2773616 java.lang.Class12: 929 2190336 [Ljava.util.WeakHashMap$Entry;13: 35298 1694304 java.util.HashMap14: 41675 1667000 java.lang.ref.Finalizer15: 65654 1575696 java.util.ArrayList16: 38729 1549160 java.util.WeakHashMap$Entry17: 36604 1171328 java.lang.ref.WeakReference18: 11786 1037168 java.lang.reflect.Method19: 25180 1007200 java.util.LinkedHashMap$Entry20: 20719 994512 com.sun.tools.javac.file.ZipFileIndex$Entry21: 60637 970192 java.lang.Object22: 994 866304 [Ljava.util.concurrent.ConcurrentHashMap$Node;24: 936 614016 io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueue26: 518 534456 [J30: 7991 447496 java.util.LinkedHashMap31: 2956 354720 org.springframework.boot.loader.jar.JarEntry35: 6551 262040 java.lang.ref.SoftReference36: 15879 254064 java.lang.Integer......我们可以通过jmap命令把堆内存的使用情况dump到文件中 [localhost /] $jmap -dump:formatb,file/home/admin/heap.hprof 1149 Dumping heap to /home/admin/heap.hprof ... Heap dump file created我们可以将文件下载下来使用 MAT工具打开文件进行分析 实战演练 我们平时遇到的内存溢出问题一般分为两种一种是由于大峰值下没有限流瞬间创建大量对象而导致的内存溢出另一种则是由于内存泄漏而导致的内存溢出。 使用限流我们一般就可以解决第一种内存溢出问题但其实很多时候内存溢出往往是内存泄漏导致的这种问题就是程序的BUG我们需要及时找到问题代码。 下面模拟了一个内存泄漏导致的内存溢出案例我们来实践一下。 ThreadLocal的作用是提供线程的私有变量这种变量可以在一个线程的整个生命周期中传递可以减少一个线程在多个函数或类中创建公共变量来传递信息避免了复杂度。但在使用时如果ThreadLocal使用不恰当就可能导致内存泄漏。 这个案例的场景就是ThreadLocal下面我们模拟对每个线程设置一个本地变量。运行以下代码系统一会儿就发送了内存溢出异常 RestController public class TestController {RequestMapping(value /test0)public String test0(HttpServletRequest request) {ThreadLocalByte[] localVariable new ThreadLocalByte[]();localVariable.set(new Byte[4096*1024]);// 为线程添加变量return success;} }在启动应用程序之前我们添加HeapDumpOnOutOfMemoryError和HeapDumpPath这两个参数开启堆内存异常日志。 java -jar -Xms1000m -Xmx4000m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/tmp/heapdump.hprof -XX:PrintGCTimeStamps -XX:PrintGCDetails -Xloggc:/tmp/heapTest.log JavaStudy-1.0-SNAPS HOT.jar通过 Jemeter 进行压测触发接口报异常。 通过日志我们很好分辨这是一个内存溢出异常。我们首先通过Linux系统命令查看进程在整个系统中内存的使用率是多少最简单就是top命令了。 再通过top -Hp pid查看具体线程占用系统资源情况。 再通过jstack pid查看具体线程的堆栈信息可以发现该线程一直处于 RUNNABLE 状态此时CPU使用率和负载并没有出现异常我们可以排除死锁或I/O阻塞的异常问题了。 我们再通过jmap查看堆内存的使用情况可以发现老年代的使用率几乎快占满了而且内存一直得不到释放 通过以上堆内存的情况我们基本可以判断系统发生了内存泄漏。下面我们就需要找到具体是什么对象一直无法回收什么原因导致了内存泄漏。 我们需要查看具体的堆内存对象看看是哪个对象占用了堆内存可以通过jmap -histo:live pid查看存活对象的数量 Byte对象占用内存明显异常说明代码中Byte对象存在内存泄漏我们在启动时已经设置了oom时的dump文件通过MAT打开dump的内存日志文件我们可以发现MAT已经提示了byte内存异常 再点击进入到Histogram页面可以查看到对象数量排序我们可以看到Byte[]数组排在了第一位选中对象后右击选择with incomming reference功能可以查看到具体哪个对象引用了这个对象。 在这里我们就可以很明显地查看到是ThreadLocal这块的代码出现了问题。 在一些比较简单的业务场景下排查系统性能问题相对来说简单且容易找到具体原因。但在一些复杂的业务场景下或是一些开源框架下的源码问题相对来说就很难排查了有时候通过工具只能猜测到可能是某些地方出现了问题而实际排查则要结合源码做具体分析。
http://www.pierceye.com/news/420624/

相关文章:

  • 手机网站 程序网站备案要关多久
  • 网站需要具备条件在线用代码做网站
  • 代码下载网站河北恒山建设集团网站
  • 网站设计应遵循的原则做企业网站有哪些好处
  • 网站不用域名解绑商务网站建设的一般流程是什么?
  • 企业网站的运营如何做秦皇岛网站制作与网站建设
  • 潍坊 营销型网站建设室内设计和装修设计
  • 滕州市东方建设工程事务有限公司网站房房网
  • php网站漂浮广告代码百度一下打开网页
  • 华为公司网站建设方案模板自己做网站的费用
  • 网站设计命名规范广州短视频内容营销平台
  • 天津专门做网站的公司成都市城乡建设局网站
  • 品牌网站升级wordpress 4.9中文
  • 网站搭建软件广告标识标牌制作公司
  • 做造价在哪个网站查价格微信小程序是什么语言开发的
  • 网站建设平台接单做电子商务平台网站需要多少钱
  • 甘肃网站seo技术厂家企业简介内容
  • 视觉中国设计网站做音乐网站
  • 金坛区建设工程质量监督网站西宁百姓网免费发布信息
  • 运维 网站开发网站如何引入流量
  • 网站建设泉州效率网络西安网站设计公司哪家好
  • 青羊建站报价网上能注册公司吗怎么注册
  • 免费网站虚拟主机整站seo技术搜索引擎优化
  • 青岛网站建设订做油画风网站
  • 网站备案名称的影响吗六安哪里有做推广网站
  • 网站建设策划书网站发布与推广长沙公司网站费用
  • 设计网页英语口碑优化seo
  • 试客那个网站做的好北京做软件开发的公司
  • 网站多套系统如何调用网页大图素材
  • 沧州网站建设方案咨询wordpress需要 伪静态