热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM篇<十>调优

一、调优的概述1.1调优的目的防止出现OOM,进行JVM规划和预调优解决程序运行中各种OOM减少FullGC出现的频率,解决运行慢、卡顿问题二、生

一、调优的概述

1.1调优的目的


  • 防止出现OOM,进行JVM规划和预调优
  • 解决程序运行中各种OOM
  • 减少Full GC出现的频率,解决运行慢、卡顿问题

二、生产环境的问题


2.1、堆溢出

原因:
1、代码中可能存在大对象分配
2、可能存在内存泄漏,导致在多次GC之后,还是无法找到一块足够大的内存容纳当前对象。

解决方法:
1、检查是否存在大对象的分配,最有可能的是大数组分配
2、通过jmap命令,把堆内存dump下来,使用MAT等工具分析一下,检查是否存在内存泄漏的问题
3、如果没有找到明显的内存泄漏,使用-Xmx加大堆内存
4、还有一点容易被忽略,检查是否有大量的自定义的 Finalizable 对象,也有可能是框架内部提供的,考虑其存在的必要性

案例:

/*** 案例1&#xff1a;模拟线上环境OOM* 参数设置:* -XX:&#43;PrintGCDetails -XX:MetaspaceSize&#61;64m* -XX:&#43;HeapDumpOnOutOfNemoryError -XX:HeapDumpPath&#61;heap/heapdump.hprof* -XX:&#43;PrintGCDateStamps -Xms30M -Xmx30M -Xloggc:log/gc-oomHeap.log**/&#64;RequestMapping("/oomTest")public void addObject(){System.err.println("oomTest"&#43;peopleSevice);ArrayList<People> people &#61; new ArrayList<>();while (true){people.add(new People());}}

2.2、元空间的溢出

原因&#xff1a;
1.运行期间生成了大量的代理类&#xff0c;导致方法区被撑爆&#xff0c;无法卸载
2.应用长时间运行&#xff0c;没有重启
3.元空间内存设置过小

解决方法&#xff1a;
1.运行期间生成了大量的代理类&#xff0c;导致方法区被撑爆&#xff0c;无法卸载
2.应用长时间运行&#xff0c;没有重启
3.元空间内存设置过小

案例&#xff1a;

/*** 案例2:模拟元空间OOM溢出*参数设置:* -XX:&#43;PrintGCDetails -XX:MetaspaceSize&#61;60m -XX:MaxMetaspaceSize&#61;60m-XSS512K -XX:&#43;HeapDumponOutOfMemoryErrorl* -XX:HeapDumpPath&#61;heap/heapdumpMeta.hprof -xx:SurvivorRatio&#61;8* -XX:&#43;TraceClassLoading -XX:&#43;TraceClassUnloading -XX:&#43;PrintGCDateStamps-Xms60M -Xmx60M -Xloggc:log/gc-oomMeta.log*/&#64;RequestMapping("/metaSpaceOom")public void metaSpaceOom(){ClassLoadingMXBean classLoadingMXBean &#61; ManagementFactory.getClassLoadingMXBean();while (true){Enhancer enhancer &#61; new Enhancer();enhancer.setSuperclass(People.class);enhancer.setUseCache(false);//enhancer.setUseCache(true);enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {System.out.println("我是加强类&#xff0c;输出print之前的加强方法");return methodProxy.invokeSuper(o,objects);});People people &#61; (People)enhancer.create();people.print();System.out.println(people.getClass());System.out.println("totalClass:" &#43; classLoadingMXBean.getTotalLoadedClassCount());System.out.println("activeClass:" &#43; classLoadingMXBean.getLoadedClassCount());System.out.println("unloadedClass:" &#43; classLoadingMXBean.getUnloadedClassCount());}}

2.3、GC overhead limit exceeded

原因&#xff1a;
这个是DK6新加的错误类型&#xff0c;一般都是堆太小导致的。Sun官方对此的定义:超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常。本质是一个预判性的异常&#xff0c;抛出该异常时系统没有真正的内存溢出

解决&#xff1a;
1&#xff0e;检查项目中是否有大量的死循环或有使用大内存的代码&#xff0c;优化代码。
2&#xff0e;添加参数-XX:-UseGCOverheadLimit禁用这个检查&#xff0c;其实这个参数解决不了内存问题&#xff0c;只是把错误的信息延后&#xff0c;最终出现 java.lang.OutOfMemoryError: Java heap
space。
3. dump内存&#xff0c;检查是否存在内存泄漏&#xff0c;如果没有&#xff0c;加大内存。

测试&#xff1a;

/**** 测试 GC overhead limit exceeded* 参数设置&#xff1a;* -XX:&#43;PrintGCDetails -XX:&#43;HeapDumpOnOutOfMemoryError* -XX:HeapDumpPath&#61;heap/dumpExceeded.hprof* -XX:&#43;PrintGCDateStamps -Xms10M -Xmx1OM-xloggc:log/gc-oomExceeded.log*/public static void main(String[] args) {test1();// test2();}public static void test1() {int i &#61; 0;List<String> list &#61; new ArrayList<>();try {while (true) {list.add(UUID.randomUUID().toString().intern());i&#43;&#43;;}} catch (Throwable e) {System.out.println("************i: " &#43; i);e.printStackTrace();throw e;}}//回收效率大于2%所以只会出现堆空间不足public static void test2() {String str &#61; "";Integer i &#61; 1;try {while (true) {i&#43;&#43;;str &#43;&#61; UUID.randomUUID();}} catch (Throwable e) {System.out.println("************i: " &#43; i);e.printStackTrace();throw e;}}

2.4 、线程溢出

注意&#xff1a;windos试不出来&#xff0c;超过windos上线会重启

线程创建公式&#xff1a;
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) &#61; Numberof threads
MaxProcessMemory 指的是进程可寻址的最大空间
VMMemory JVM内存
ReservedOsMemory 保留的操作系统内存
ThreadStackSize 线程栈的大小

注意&#xff1a;在32位操作系统下当前公式遵守的 在64位操作系统下MaxProcessMemory &#xff08;最大寻址空间&#xff09;这个值接近无限大&#xff0c;所以ThreadStackSize不影响公式的值

Linux查看线程数&#xff1a;
cat /proc/sys/kernel/pid_max 系统最大pid值&#xff0c;在大型系统里可适当调大
cat /proc/sys/kernel/threads-max 系统允许的最大线程数
maxuserprocess (ulimit -u&#xff09;系统限制某用户下最多可以运行多少进程或线程
cat /proc/sys/vm/max_map_count

在这里插入图片描述
程序

public class TestNativeOutOfMemoryError {public static void main(String[] args) {for (int i &#61; 0; ; i&#43;&#43;) {System.out.println("i &#61; " &#43; i);new Thread(new HoldThread()).start();}}
}class HoldThread extends Thread {CountDownLatch cdl &#61; new CountDownLatch(1);&#64;Overridepublic void run() {try {cdl.await();} catch (InterruptedException e) {}}
}

三、性能优化


3.1性能监控

一种以非强行或者入侵方式收集或查看应用运营性能数据的活动。
监控前&#xff0c;设置好回收器组合&#xff0c;选定CPU&#xff08;主频越高越好&#xff09;&#xff0c;设置年代比例&#xff0c;设置日志参数&#xff08;生产环境中通常不会只设置一个日志文件&#xff09;。比如:
-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log
-XX:&#43;UseGCLogFileRotation
-XX:NumberOfGCLogFiles&#61;5
-XX:GCLogFileSize&#61;20M
-XX:&#43;PrintGCDetails
-XX:&#43;PrintGCDateStamps
-XX:&#43;PrintGcCause

问题&#xff1a;


  • GC频繁
  • cpu load过高
  • OOM
  • 内存泄漏
  • 死锁
  • 程序响应时间过长

3.2性能分析


  • 打印GC日志&#xff0c;通过GCviewer或者http://gceasy.io来分析日志信息
  • 灵活运用命令行工具&#xff0c;jstack, jmap&#xff0c; jinfo等
  • dump出堆文件&#xff0c;使用内存分析工具分析文件&#xff08;jconsole/jvisualvm/jprofiler&#xff09;
  • 使用阿里Arthas&#xff0c;或jconsole&#xff0c;JVisualVM来实时查看JVM状态
  • jstack查看堆栈信息

3.3性能调优


  • 适当增加内存,根据业务背景选择垃圾回收器
  • 优化代码&#xff0c;控制内存使用
  • 增加机器&#xff0c;分散节点压力
  • 合理设置线程池线程数量
  • 使用中间件提高程序效率&#xff0c;比如缓存&#xff0c;消息队列等

四、优化案例


4.1调整堆内存大小提高性能

测试参数设置&#xff1a;
setenv.sh文件中写入&#xff08;大小根据自己情况修改): setenv.sh内容如下:

export CATALINA_OPTS&#61;"$CATALINA_OPTS -Xms30m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:SurvivorRatio&#61;8"
export CATALINA_OPTS&#61;"$CATALINA_OPTS-Xmx30m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;UseParallelGC"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDetails"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:MetaspaceSize&#61;64m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDateStamps"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -Xloggc:/opt/tomcat8.5/logs/gc.log"

打印信息

//查看运行进程id
jps
//查看运行信息
jstat -gc 进程id 间隔时间&#xff08;毫秒&#xff09;次数
例如 jstat -gc 5397 1000 5

在这里插入图片描述

优化参数&#xff08;调大堆内存&#xff09;

export CATALINA_OPTS&#61;"$CATALINA_OPTS -xms120m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:SurvivorRatio&#61;8"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -Xmx120m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;UseParallelGC"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDetails"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:MetaspaceSize&#61;64m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDateStamps"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -Xloggc:/opt/tomcat8.5/logs/gc.log"

在这里插入图片描述
结果&#xff1a;FullGC次数大幅降低


4.2JIT编译器的优化


  • 逃逸分析&#xff1a;当前方法内new的对象被当前方法外所使用
  • 栈上分配
    • 将堆分配转化为栈分配。如果经过逃逸分析后发现&#xff0c;一个对象并没有逃逸出方法的话&#xff0c;那么就可能被优化成栈上分配。这样就无需在堆上分配内存&#xff0c;也无须进行垃圾回收了。可以减少垃圾回收时间和次数。
    • JIT编译器在编译期间根据逃逸分析的结果&#xff0c;发现如果一个对象并没有逃逸出方法的话&#xff0c;就可能被优化成栈上分配。分配完成后&#xff0c;继续在调用栈内执行&#xff0c;最后线程结束&#xff0c;栈空间被回收&#xff0c;局部变量对象也被回收。这样就无须进行垃圾回收了。

4.2.1栈上分配测试&#xff08;并没有分配一个个对象&#xff0c;而是用变量替换去体现出来&#xff09;

/*** 栈上分配测试* -Xmx1G -Xms1G -XX:-DoEscapeAnalysis -XX:&#43;PrintGCDetails** 只要开启了逃逸分析&#xff0c;就会判断方法中的变量是否发生了逃逸。如果没有发生了逃逸&#xff0c;则会使用栈上分配*/
public class StackAllocation {public static void main(String[] args) {long start &#61; System.currentTimeMillis();for (int i &#61; 0; i < 10000000; i&#43;&#43;) {alloc();}// 查看执行时间long end &#61; System.currentTimeMillis();System.out.println("花费的时间为&#xff1a; " &#43; (end - start) &#43; " ms");// 为了方便查看堆内存中对象个数&#xff0c;线程sleeptry {Thread.sleep(1000000);} catch (InterruptedException e1) {e1.printStackTrace();}}private static void alloc() {User user &#61; new User();//是否发生逃逸&#xff1f; 没有&#xff01;}static class User {}
}

jdk6之后默认开启栈上分配,测试需要关闭
-XX:-DoEscapeAnalysis

关闭栈上分配测试&#xff08;会在堆内存中分配对象&#xff09;
在这里插入图片描述

开启栈上分配测试
在这里插入图片描述


4.2.2同步消除

同步消除。如果一个对象被发现只能从一个线程被访问到&#xff0c;那么对于这个对象的操作可以不考虑同步。


  • 线程同步的代价是相当高的&#xff0c;同步的后果是降低并发性和性能。
  • 在动态编译同步块的时候&#xff0c;JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果没有&#xff0c;那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这样就能大大提高并发性和性能。这个取消同步的过程就叫同步省略&#xff0c;也叫锁消除。

public class SynchronizedTest {public void f() {/** 代码中对hollis这个对象进行加锁&#xff0c;但是hollis对象的生命周期只在f()方法中&#xff0c;* 并不会被其他线程所访问到&#xff0c;所以在JIT编译阶段就会被优化掉。** 问题&#xff1a;字节码文件中会去掉hollis吗&#xff1f;* 不会&#xff0c;因为只会由解释器&#xff0c;不会经过JIT编译器* */Object hollis &#61; new Object();synchronized(hollis) {System.out.println(hollis);}/** 优化后&#xff1b;* Object hollis &#61; new Object();* System.out.println(hollis);* */}
}

4.2.3标量替换

标量(Scalar&#xff09;是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量

相对的&#xff0c;那些还可以分解的数据叫做聚合量&#xff08;Aggregate) ,Java中的对象就是聚合量&#xff0c;因为他可以分解成其他聚合量和标量。

在JIT阶段&#xff0c;如果经过逃逸分析&#xff0c;发现一个对象不会被外界访问的话&#xff0c;那么经过JIT优化&#xff0c;就会把这个对象拆解成若干个其中包含的若干个成员变量来代替。这个过程就是标量替换。

参数设置&#xff08;默认开启&#xff09;true
-XX:&#43;EliminateAllocations:

代码体现&#xff1a;

public static void main (string [ ] args){alloc ( ) ;
}
private static void alloc (){Point point &#61; new Point ( 1,2&#xff09;;system.out.println ( "point.x&#61;"&#43;point.x&#43;"; point.y&#61;"&#43;point.y);
}
class Point {private int x;private int y;以上代码&#xff0c;经过标量替换后&#xff0c;就会变成:private static void alloc() {int x &#61; l;Iint y &#61; 2;System.out.println ( "point.x&#61;"&#43;x&#43;"; point.y&#61;"&#43;y);
}

测试代码

/*** 标量替换测试* -Xmx100m -Xms100m -XX:&#43;DoEscapeAnalysis -XX:&#43;PrintGCDetails -XX:-EliminateAllocations** 结论&#xff1a;Java中的逃逸分析&#xff0c;其实优化的点就在于对栈上分配的对象进行标量替换。** &#64;author shkstart shkstart&#64;126.com* &#64;create 2021 12:01*/
public class ScalarReplace {public static class User {public int id;public String name;}public static void alloc() {User u &#61; new User();//未发生逃逸u.id &#61; 5;u.name &#61; "www.atguigu.com";}public static void main(String[] args) {long start &#61; System.currentTimeMillis();for (int i &#61; 0; i < 10000000; i&#43;&#43;) {alloc();}long end &#61; System.currentTimeMillis();System.out.println("花费的时间为&#xff1a; " &#43; (end - start) &#43; " ms");}
}

逃逸分析总结:


  • 关于逃逸分析的论文在1999年就已经发表了&#xff0c;但直到DK 1.6才有实现&#xff0c;而且这项技术到如今也并不是十分成熟的。
  • 其根本原因就是无法保证非逃逸分析的性能消耗一定能高于他的消耗。虽然经过逃逸分析可以做标量替换、栈上分配、和锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的&#xff0c;这其实也是一个相对耗时的过程。
  • 一个极端的例子&#xff0c;就是经过逃逸分析之后&#xff0c;发现没有一个对象是不逃逸的。那这个逃逸分析的过程就白白浪费掉了。
  • 虽然这项技术并不十分成熟&#xff0c;但是它也是即时编译器优化技术中一个十分重要的手段。
  • 注意到有一些观点&#xff0c;认为通过逃逸分析&#xff0c;JVM会在栈上分配那些不会逃逸的对象&#xff0c;这在理论上是可行的&#xff0c;但是取决于JVM设计者的选择。
  • 目前很多书籍还是基于JDK7以前的版本&#xff0c;JDK已经发生了很大变化&#xff0c; intern字符串的缓存和静态变量
  • 曾经都被分配在永久代上&#xff0c;而永久代已经被元数据区取代。但是&#xff0c;intern字符串缓存和静态变量并不是被转移到元数据区&#xff0c;而是直接在堆上分配&#xff0c;所以这一点同样符合前面一点的结论:对象实例都是分配在堆上。

4.3合理分配堆内存


4.3.1参数设置


  • Java整个堆大小设置&#xff0c;Xmx和 Xms设置为老年代存活对象的3-4倍&#xff0c;即FullGC之后的老年代内存占用的3-4倍。
  • 方法区&#xff08;永久代 PermSize和MaxPermSize或元空间MetaspaceSize和MaxMetaspaceSize&#xff09;设置为老年代存活对象的1.2-1.5倍。
  • 年轻代Xmn的设置为老年代存活对象的1-1.5倍。

4.3.2老年代存活大小的计算


  • JVM参数中添加GC日志&#xff0c;GC日志中会记录每次FullGC之后各代的内存大小&#xff0c;观察老年代GC之后的空间大小。可观察一段时间内&#xff08;比如2天&#xff09;的FullGC之后的内存情况&#xff0c;根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小(可根据多次FullGC之后的内存大小取平均值)

强制触发Full GC的方法
1、jmap -dump:live,format&#61;b,file&#61;heap.bin 将当前的存活对象dump到文件&#xff0c;此时会触发FullGc
2、jmap -histo:live 打印每个class的实例数目,内存占用,类全名信息.live子参数加上后,只统计活的对象数量.此时会触发FullGd
3、在性能测试环境&#xff0c;可以通过Java监控工具来触发FullGC&#xff0c;比如使用VisualVM和3Console,VisualVM集成了JConsole&#xff0c;VisualVM或者JConsole上面有一个触发GC的按钮。

估算GC频率
比如从数据库获取一条数据占用128个字节&#xff0c;需要获取1000条数据&#xff0c;那么一次读取到内存的大小就是128 B/1024 Kb/1024M&#xff09; * 1000 &#61; 0.122M&#xff0c;那么我们程序可能需要并发读取&#xff0c;比如每秒读取100次&#xff0c;那么内存占用就是0.122100 &#61; 12.2M&#xff0c;如果堆内存设置1个G&#xff0c;那么年轻代大小大约就是333M&#xff0c;那么333M80%/12.2M &#61;21.84s &#xff0c;也就是说我们的程序几乎每分钟进行两到三次youngGC。


4.4调整ParallelGC比例

ParallelGC默认是6:1:1
调整参数设置


  • -XX:&#43;SusvivorRatio:8
  • -XX:&#43;UseAdaptivesizePolicy(自动调整策略)

注意&#xff1a;对于面向外部的大流量、低延迟系统&#xff0c;不建议启用此参数&#xff0c;建议关闭该参数。


4.5CPU占用很高排查方案

1、ps aux / grep java 查看到当前java进程使用cpu、内存、磁盘的情况获取使用量异常的进程
2、top -Hp 进程pid检查当前使用异常线程的pid
3、把线程pid变为16进制如31695-》 7bcf 然后得到Ox7bcf
4、查看信息&#xff08;2种方式&#xff09;


  • 1.jstack&#43;进程的pid l grep -A20 Ox7bcf得到相关进程的代码
  • 2.将信息打印到文件中 jstack pid > 文件名

4.6G1线程的并发执行线程数对性能的影响

测试参数设置

export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;UseG1GC"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -xms 30m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -xm×30m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDetails"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:MetaspaceSize&#61;64m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDateStamps"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -Xloggc :/opt/tomcat8.5/logs/gc.log"
export CATALINA_OPTS&#61;"$CATALINA_OPTS-XX:ConcGCThreads&#61;1"

在这里插入图片描述

增加线程数会增大吞吐量&#xff08;-XX:ConcGCThreads设置为2效果和4,8差不多 因为最多为并行垃圾回收的1/4&#xff09;

export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;UseG1GC"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -xms 30m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -xm×30m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDetails"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:MetaspaceSize&#61;64m"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:&#43;PrintGCDateStamps"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -Xloggc :/opt/tomcat8.5/logs/gc.log"
export CATALINA_OPTS&#61;"$CATALINA_OPTS -XX:ConcGCThreads&#61;2"

4.7调整垃圾回收器对提高服务器的影响

根据服务器的cpu和性能合理使用垃圾回收器


4.8百万级的交易系统如何设置JVM参数

在这里插入图片描述
响应时间控制在100ms怎么保证&#xff1f;
做压测控制延迟时间


推荐阅读
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 本文探讨了 Java 中 Pair 类的历史与现状。虽然 Java 标准库中没有内置的 Pair 类,但社区和第三方库提供了多种实现方式,如 Apache Commons 的 Pair 类和 JavaFX 的 javafx.util.Pair 类。这些实现为需要处理成对数据的开发者提供了便利。此外,文章还讨论了为何标准库未包含 Pair 类的原因,以及在现代 Java 开发中使用 Pair 类的最佳实践。 ... [详细]
  • 设计实战 | 10个Kotlin项目深度解析:首页模块开发详解
    设计实战 | 10个Kotlin项目深度解析:首页模块开发详解 ... [详细]
  • Node.js 教程第五讲:深入解析 EventEmitter(事件监听与发射机制)
    本文将深入探讨 Node.js 中的 EventEmitter 模块,详细介绍其在事件监听与发射机制中的应用。内容涵盖事件驱动的基本概念、如何在 Node.js 中注册和触发自定义事件,以及 EventEmitter 的核心 API 和使用方法。通过本教程,读者将能够全面理解并熟练运用 EventEmitter 进行高效的事件处理。 ... [详细]
  • 如何使用 net.sf.extjwnl.data.Word 类及其代码示例详解 ... [详细]
  • 全面解析Java虚拟机:内存模型深度剖析 ... [详细]
  • 深入浅析JVM垃圾回收机制与收集器概述
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》的阅读心得进行整理,详细探讨了JVM的垃圾回收机制及其各类收集器的特点与应用场景。通过分析不同垃圾收集器的工作原理和性能表现,帮助读者深入了解JVM内存管理的核心技术,为优化Java应用程序提供实用指导。 ... [详细]
  • Squaretest:自动生成功能测试代码的高效插件
    本文将介绍一款名为Squaretest的高效插件,该工具能够自动生成功能测试代码。使用这款插件的主要原因是公司近期加强了代码质量的管控,对各项目进行了严格的单元测试评估。Squaretest不仅提高了测试代码的生成效率,还显著提升了代码的质量和可靠性。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • 技术分享:深入解析GestureDetector手势识别机制
    技术分享:深入解析GestureDetector手势识别机制 ... [详细]
  • 深入解析 Vue 中的 Axios 请求库
    本文深入探讨了 Vue 中的 Axios 请求库,详细解析了其核心功能与使用方法。Axios 是一个基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js 环境。文章首先介绍了 Axios 的基本概念,随后通过具体示例展示了如何在 Vue 项目中集成和使用 Axios 进行数据请求。无论你是初学者还是有经验的开发者,本文都能为你解决 Vue.js 相关问题提供有价值的参考。 ... [详细]
  • 本书详细介绍了在最新Linux 4.0内核环境下进行Java与Linux设备驱动开发的全面指南。内容涵盖设备驱动的基本概念、开发环境的搭建、操作系统对设备驱动的影响以及具体开发步骤和技巧。通过丰富的实例和深入的技术解析,帮助读者掌握设备驱动开发的核心技术和最佳实践。 ... [详细]
  • 本文深入探讨了RecyclerView的缓存与视图复用机制,详细解析了不同类型的缓存及其功能。首先,介绍了屏幕内ViewHolder的Scrap缓存,这是一种最轻量级的缓存方式,旨在提高滚动性能并减少不必要的视图创建。通过分析其设计原理,揭示了Scrap缓存为何能有效提升用户体验。此外,还讨论了其他类型的缓存机制,如RecycledViewPool和ViewCacheExtension,进一步优化了视图复用效率。 ... [详细]
  • 本文将详细介绍在Android应用中添加自定义返回按钮的方法,帮助开发者更好地理解和实现这一功能。通过具体的代码示例和步骤说明,本文旨在为初学者提供清晰的指导,确保他们在开发过程中能够顺利集成返回按钮,提升用户体验。 ... [详细]
author-avatar
梅爱敏_629
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有