Java性能调优 - JVM性能监测及调优
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 value=123
,会在准备阶段分配内存,并初始化值为123,而如果是private static int value=123
,这个阶段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:MaxFreqInlineSize=N
来设置大小值; - 不经常执行的方法,默认情况下,方法大小小于35字节才会进行内联,我们也可以通过
-XX:MaxInlineSize=N
来重置大小值。
热点方法的优化可以有效提高系统性能,一般我们可以通过以下几种方式来提高方法内联:
- 通过设置
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.234+0800)
-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回收器,
CMS(Concurrent Mark Sweep)回收器
和G1回收器
都是不错的选择。 - 当我们的对系统吞吐量有要求时,就可以选择
Parallel Scavenge
回收器来提高系统的吞吐量。
- 当我们对停顿时间有要求,一般会选择响应速度较快的GC回收器,
优化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) {List<Byte[]> temp = new ArrayList<Byte[]>();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:SurvivorRatio=8将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 0
- r:等待运行的进程数;
- 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
:虚拟地址大小,虚拟内存使用KB;RSS
:常驻集合大小,非交换区内存使用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.717
S0C
:年轻代中To Survivor的容量(单位KB);S1C
:年轻代中From Survivor的容量(单位KB);S0U
:年轻代中To Survivor目前已使用空间(单位KB);S1U
:年轻代中From Survivor目前已使用空间(单位KB);EC
:年轻代中Eden的容量(单位KB);EU
:年轻代中Eden目前已使用空间(单位KB);OC
:Old代的容量(单位KB);OU
:Old代目前已使用空间(单位KB);MC
:Metaspace的容量(单位KB);MU
:Metaspace目前已使用空间(单位KB);YGC
:从应用程序启动到采样时年轻代中gc次数;YGCT
:从应用程序启动到采样时年轻代中gc所用时间(s);FGC
:从应用程序启动到采样时old代(全gc)gc次数;FGCT
:从应用程序启动到采样时old代(全gc)gc所用时间(s);GCT
:从应用程序启动到采样时gc用的总时间(s)。
jstack命令
jstack
是一种线程堆栈分析工具,最常用的功能就是使用 jstack pid
命令查看线程的堆栈信息,通常会结合top -Hp pid
或 pidstat -p pid -t
一起查看具体线程的状态,也经常用来排查一些死锁的异常。
[admin@nui-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 prio=5 os_prio=0 tid=0x00007fda90395000 nid=0x31a24 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 prio=5 os_prio=0 tid=0x00007fda90394000 nid=0x31a23 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 等状态)以及是否持有锁等。
nid=0x31a24
为线程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
则只统计活对象:
[admin@nui-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:format=b,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) {ThreadLocal<Byte[]> localVariable = new ThreadLocal<Byte[]>();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
功能,可以查看到具体哪个对象引用了这个对象。
在这里我们就可以很明显地查看到是ThreadLoca
l这块的代码出现了问题。
在一些比较简单的业务场景下,排查系统性能问题相对来说简单,且容易找到具体原因。但在一些复杂的业务场景下,或是一些开源框架下的源码问题,相对来说就很难排查了,有时候通过工具只能猜测到可能是某些地方出现了问题,而实际排查则要结合源码做具体分析。
相关文章:
Java性能调优 - JVM性能监测及调优
JVM 内存模型概述 堆 堆是JVM内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由From Survivor和To Su…...
[代码随想录21二叉树]二叉树的修改和改造,修剪二叉树,将有序数组转为二叉搜索树
前言 二叉树章节最后的题目了,就是对搜索二叉树的改造, 题目链接 669. 修剪二叉搜索树 - 力扣(LeetCode) 108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode) 一、修剪二叉搜索树 思路:等会…...
C# OpenCV机器视觉:图像平滑
在一个寒冷的冬日,阿强窝在家里的沙发上,裹着厚厚的毛毯,手里捧着一杯热巧克力。他的朋友们约他一起去滑雪,但阿强却更喜欢待在温暖的家中,享受这份宁静。突然,他的手机响了,是朋友们发来的滑雪…...
Dubbo 3.x源码(26)—Dubbo服务引用源码(9)应用级服务发现订阅refreshServiceDiscoveryInvoker
基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了MigrationRuleHandler这个处理器,它用于通过动态更改规则来控制迁移行为。MigrationRuleListener的onrefer方法是Dubbo2.x 接口级服务发现与Dubbo3.x应用级服务发现之间迁移的关键…...
java client http请求 返回数据 实时循环监听 url 中资源是否生成
1、php 中 执行 exec 调用操作系统 命令行 执行 以下 java 代码 生成 的jar 2、php 执行命令是 以上1 需要命令行 输入 参数 taskid 3、实现实时监听 MP3 url 是否生成 4、 package com.example.filedemo.controller;import java.io.BufferedReader; import java.io.InputStre…...
ONES 功能上新|ONES Copilot、ONES Wiki 新功能一览
ONES Copilot 可基于工作项的标题、描述、属性信息,对工作项产生的动态和评论生成总结。 针对不同类型的工作项,总结输出的内容有对应的侧重点。 应用场景: 在一些流程步骤复杂、上下游参与成员角色丰富的场景中,工作项动态往往会…...
【自适应】postcss-pxtorem适配Web端页面
在进行页面开发时,自适应设计是一个关键的考虑因素。为了实现这一点,postcss-pxtorem是一个非常有用的工具,它可以将CSS中的px单位转换为rem单位,从而实现基于根元素字体大小的自适应布局。下面介绍一下在项目中如何引入并配置pos…...
BOE(京东方)“向新2025”年终媒体智享会首站落地上海 六大维度创新开启产业发展新篇章
12月17日,BOE(京东方)以“向新2025”为主题的年终媒体智享会在上海启动。正值BOE(京东方)新三十年的开局之年,活动全面回顾了2024年BOE(京东方)在各领域所取得的领先成果,深度解读了六大维度的“向新”发展格局,同时详细剖析了BOE(京东方)在智能制造领域的领先实践。BOE(京东方…...
Moretl安全日志采集工具
永久免费: 至Gitee下载 使用教程: Moretl使用说明 使用咨询: 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架…...
LabVIEW农机自主导航监控系统
随着现代农业技术的快速发展,自主导航农机的需求日益增加,提高作业效率和减少劳动成本成为农业现代化的关键目标。本文介绍了一个基于LabVIEW的农机自主导航监控系统的开发案例,该系统通过先进的传感器与控制技术,实现农机在田间作…...
ChatGPT重大更新:新增实时搜索和高级语音
12月17日消息,据报道,OpenAI开启了第八天技术分享直播,对ChatGPT搜索功能进行了大量更新。 此次ChatGPT新增的功能亮点纷呈。其中,实时搜索功能尤为引人注目。OpenAI对搜索算法进行了深度优化,使得用户提出问题后&…...
爬虫基础学习
爬虫概念与工作原理 爬虫是什么:爬虫(Web Scraping)是自动化地访问网站并提取数据的技术。它模拟用户浏览器的行为,通过HTTP请求访问网页,解析HTML文档并提取有用信息。 爬虫的基本工作流程: 发送HTTP请求…...
一般行业安全管理人员考试题库分享
1.在高速运转的机械飞轮外部安装防护罩,属于(B)安全技术措施。 A.限制能量 B.隔离 C.故障设计 D.设置薄弱环节 2.生产经营单位的(B)是本单位安全生产的第一责任人,对落实本单位安全生产主体责任全面负责,具体履行安全生产管理职责。 A.全员 B…...
递归问题(c++)
递归设计思路 数列递归 : 如果一个数列的项与项之间存在关联性,那么可以使用递归实现 ; 原理 : 如果一个函数可以求A(n),那么该函数就可以求A(n-1),就形成了递归调用 ; 注意: 一般起始项是不需要求解的,是已知条件 这就是一个典型…...
企业数字化转型规划“秘籍”全解析
一、规划前奏:明确目标与洞察现状 (一)描绘数字化转型愿景 数字化转型愿景是工程设计总承包企业未来发展的蓝图,是企业数字化征程的指引。它不仅涉及技术更新,更是企业战略、运营模式和组织文化的深度重塑。企业需确保…...
达梦8-达梦数据的示例用户和表
1、示例库说明: 创建达梦数据的示例用户和表,导入测试数据。 在完成达梦数据库的安装之后,在/opt/dmdbms/samples/instance_script目录下有用于创建示例用户的SQL文件。samples目录前的路径根据实际安装情况进行修改,本文将达梦…...
day08-别名-重定向-去重排序等
1.重复用touch命令创建同一份文件,会修改文件的时间戳。 alias命令: 别名 查看已有别名:alias [rootoldboy ~]# alias alias cpcp -i alias egrepegrep --colorauto alias fgrepfgrep --colorauto alias grepgrep --colorauto alias l.ls…...
如何在 .NET Core 中轻松实现异步编程并提升性能
目录 初识异步编程 与多线程关系 异步编程操作 初识异步编程 异步编程:是指在执行某些任务时程序可以在等待某个操作完成的过程中继续执行其他任务,而不是阻塞当前线程,这在处理I/O密集型操作(如文件读取、数据库查询、网络请求等)时尤为重…...
makefile文件
简介: 自动化编译:只需要一个make命令,整个工程自动编译 提高编译效率:再次编译时,只编译修改的文件(查看时间戳,根据修改文件的时间判断文件是否被修改) 基本语法: …...
MybatisPlus使用LambdaQueryWrapper更新时 int默认值问题
问题: User user new User();user.setBalance(1000);QueryWrapper<User> queryWrapper new QueryWrapper<>();queryWrapper.eq("username","Jack");userMapper.update(user, queryWrapper);通过用户名,更新金额&…...
泷羽sec学习打卡-brupsuite7搭建IP炮台
声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-Brup-IP炮台搭建 搭建炮台服务端安装zmap1、更新系统和安装基础依赖ÿ…...
WPF系列一:窗口设置无边框
WindowStyle 设置:WindowStyle"None",窗口无法拖拽,但可纵向和横向拉伸 <Window x:Class"WPFDemo.MainWindow.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x&quo…...
《C++与 Armadillo:线性代数助力人工智能算法简化之路》
在人工智能领域,线性代数运算可谓是构建各类模型与算法的基石。从神经网络中的矩阵乘法、向量运算,到数据处理中的特征分解、奇异值分解等,无一不依赖高效且精准的线性代数计算。而 C作为一种强大且高效的编程语言,在人工智能开发…...
【新界面】基于卷积神经网络的垃圾分类(Matlab)
基于CNN的垃圾识别与分类GUI【新界面】 有需要可直接联系我,基本都在在线,能秒回!可加我看演示视频,不懂可以远程教学 1.此项目设计包括两份完整的源代码,有GUI界面的代码和无GUI界面系统的代码。 (以下部…...
阿尔茨海默症数据集,使用yolo,voc,coco格式对2013张原始图片进行标注,可识别轻微,中等和正常的症状
阿尔茨海默症数据集,使用yolo,voc,coco格式对2013张原始图片进行标注,可识别轻微,中等,严重和正常的症状 数据集分割 训练组100% 2013图片 有效集% 0图片 测试集…...
评估二分类模型性能之AUC-ROC 曲线
AUC-ROC 曲线 是评估二分类模型性能的重要工具。它结合了 受试者工作特性曲线 (Receiver Operating Characteristic, ROC) 和 曲线下面积 (Area Under the Curve, AUC),全面衡量分类器在不同阈值下的表现。 概念解释 1. ROC 曲线 ROC 曲线展示了分类器在不同阈值下…...
睡岗和玩手机数据集,4653张原始图,支持YOLO,VOC XML,COCO JSON格式的标注
睡岗和玩手机数据集,4653张原始图,支持YOLO,VOC XML,COCO JSON格式的标注 数据集分割 训练组70% 3257图片 有效集20% 931图片 测试集10% 465图片 预处理 没有采用任何预处…...
景联文科技:精准语音标注,驱动语音技术新发展
在人工智能迅速发展的今天,语音技术的应用已经渗透到我们生活的方方面面。从智能音箱、语音助手到自动语音识别系统,高质量的语音数据是这些应用成功的关键。景联文科技作为领先的AI数据服务提供商,专注于为客户提供高精度、高效的语音标注服…...
Linux 查看目录命令 ls 详细介绍
Linux 和 Unix 系统中 ls 命令是用于列出目录内容。用户可以查看指定目录下的文件和子目录,还可以获取有关这些文件和子目录的详细信息。 基本语法: ls [选项] [目录]如果不指定目录,ls 将列出当前工作目录下的内容。 01、-a 或 --all ls…...
Flux Tools 结构简析
Flux Tools 结构简析 BFL 这次一共发布了 Canny、Depth、Redux、Fill 四个 Tools 模型系列,分别对应我们熟悉的 ControlNets、Image Variation(IP Adapter)和 Inpainting 三种图片条件控制方法。虽然实现功能是相同的,但是其具体…...
从零开始:PHP基础教程系列-第13篇:构建简单的Web应用
从零开始:PHP基础教程系列 第13篇:构建简单的Web应用 在本篇文章中,我们将学习如何使用PHP构建一个简单的Web应用。这个应用将实现用户注册和登录功能,并使用PDO与MySQL数据库进行交互。我们将逐步实现这个应用的基本功能。 一…...
文件夹属性变0字节:全面解析与恢复指南
一、文件夹属性变0字节现象概述 在日常使用电脑的过程中,我们可能会遇到文件夹属性突然变为0字节的情况。这意味着文件夹中的文件列表或元数据被某种方式清空或损坏,导致系统无法正确读取文件夹的内容。当您尝试打开此类文件夹时,通常会收到…...
PDFMathTranslate 一个基于AI优秀的PDF论文翻译工具
PDFMathTranslate 是一个设想中的工具,旨在翻译PDF文档中的数学内容。以下是这个工具的主要特点和使用方法: 功能特点 数学公式识别:利用先进的OCR(光学字符识别)技术,精准识别PDF文档中的数学公式和文本…...
35. Three.js案例-创建带阴影的球体与平面
35. Three.js案例-创建带阴影的球体与平面 实现效果 知识点 WebGLRenderer WebGLRenderer 是Three.js中用于渲染场景的主要类之一,它负责将场景中的对象渲染到画布上。 构造器 new THREE.WebGLRenderer(parameters : Object) 参数类型描述parametersObject可选…...
【Linux】深入理解进程信号机制:信号的产生、捕获与阻塞
🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C系列》《Linux系列》《算法系列》 ⛰️ 时间不语,却回答了所有问题 目录 📚前言 📚一、信号的本质 📖1.异步通信 📖2.信…...
Vue3.0使用JavaScript脚本实现Vue Router路由:页面跳转、获取URL参数
Vue 使用 Vue Router 路由系列文章: 《Vue使用Vue Router路由:开发单页应用》 《Vue使用Vue Router路由:通过URL传递与获取参数》 《Vue3.0使用JavaScript脚本实现Vue Router路由:页面跳转、获取URL参数》 1、路由基础 在单页 Web 应用中,整个项目只有一个 HTML 文件,不…...
2025山东科技大学考研专业课复习资料一览
[冲刺]2025年山东科技大学020200应用经济学《814经济学之西方经济学[宏观部分]》考研学霸狂刷870题[简答论述计算题]1小时前[强化]2025年山东科技大学085600材料与化工《817物理化学》考研强化检测5套卷22小时前[冲刺]2025年山东科技大学030100法学《704综合一[法理学、国际法学…...
lambda 表达式 闭包写法
lambda 表达式 1.用于 匿名委托函数表达 2.用于linq 查询表达 匿名方法表达 (参数)》{ 逻辑} 比如 (x,y)>{return xy;} 如果一个参数可不带(),如果逻辑简单可以不{} 比如 x>x 如果没有参…...
什么是正则化?Regularization: The Stabilizer of Machine Learning Models(中英双语)
正则化:机器学习模型的稳定器 1. 什么是正则化? 正则化(Regularization)是一种在机器学习模型训练中,通过约束模型复杂性以防止过拟合的技术。 它的核心目标是让模型不仅在训练集上表现良好,还能在测试集上…...
【西门子PLC.博途】——面向对象编程及输入输出映射FC块
当我们做面向对象编程的时候,需要用到输入输出的映射。这样建立的变量就能够被复用,从而最大化利用了我们建立的udt对象。 下面就来讲讲映射是什么。 从本质上来说,映射就是拿实际物理对象对应程序虚拟对象,假设程序对象是I0.0&…...
CS61a.1 textbook1.2 编程要素
1.structure and interpretation of computer programs Python 内置了对各种常见编程活动的支持, 例如,操作文本、显示图形以及通过 互联网。Python 代码行 >>> from urllib.request import urlopen是一个 import 语句,用于加载用…...
计算机毕业设计Django+Tensorflow音乐推荐系统 音乐可视化 卷积神经网络CNN LSTM音乐情感分析 机器学习 深度学习 Flask
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
使用国内镜像网站在线下载安装Qt(解决官网慢的问题)——Qt
国内镜像网站 中国科学技术大学:http://mirrors.ustc.edu.cn/qtproject/清华大学:https://mirrors.tuna.tsinghua.edu.cn/qt/北京理工大学:http://mirror.bit.edu.cn/qtproject/ 南京大学:https://mirror.nju.edu.cn/qt腾讯镜像&…...
乳腺癌多模态诊断解释框架:CNN + 可解释 AI 可视化
乳腺癌多模态诊断解释框架:CNN 可解释 AI 可视化 论文大纲理解1. 确认目标2. 分析过程(目标-手段分析)3. 实现步骤4. 效果展示 结构分析1. 层级结构分析叠加形态(从基础到高级)构成形态(部分到整体&#x…...
MySQL篇之对MySQL进行参数优化,提高MySQL性能
1. MySQL参数优化说明 MySQL 参数调优是提高数据库性能的重要手段之一。通过调整 MySQL 的配置参数,可以优化查询速度、提升并发处理能力、减少资源消耗等。 MySQL 的性能优化涉及到多个方面,包括内存管理、磁盘 I/O、查询优化、连接管理、复制配置等。…...
Scratch节日 | 快乐圣诞节——用编程传递节日祝福! ✨
今天为大家推荐一款充满节日气氛的Scratch项目——《快乐圣诞节》!这款圣诞主题动画贺卡项目不仅让小朋友们学习编程知识,还提供了一种用创意传递祝福的方式。通过编程打造星星闪烁的圣诞树,播放经典圣诞音乐,制作一张属于自己的节…...
android studio更改应用图片,和应用名字。
更改应用图标,和名字 先打开AndroidManifest.xml文件。 更改图片文件名字( 右键-->构建-->重命名(R))...
PHP8.4下webman直接使用topthink/think-orm
环境信息 操作系统win11php 8.4.1webman-framework ^1.6.8MySQL 8.4.3topthink/think-orm ^3.0 说明 PHP8.3以下版本 直接使用webman提供的webman/think-orm更方便。 PHP 环境换为 8.4 使用webman/think-orm 报了个错;所以换topthink/think-orm,根据文…...
uniapp 微信小程序 功能入口
单行单独展示 效果图 html <view class"shopchoose flex jsb ac" click"routerTo(要跳转的页面)"><view class"flex ac"><image src"/static/dyd.png" mode"aspectFit" class"shopchooseimg"&g…...
Halcon 机器视觉案例 之 连接件测量
第一篇 机器视觉案例 之 连接件测量 文章目录 第一篇 机器视觉案例 之 连接件测量1.案例要求2.实现思路2.1 读取单张图片并创建图像模板2.2 画出圆和直线2.3 创建测量模型2.4 循环读取多张图片并查找图像中连接件位置2.5 根据偏移量补偿使得测量模型移动至指定位置 3.实现效果4…...