首页
统计
友链
关于
Search
1
静静地生活着
382 阅读
2
JVM_1.引言
361 阅读
3
Chapter 03
299 阅读
4
机器学习 01
282 阅读
5
欢迎使用 Typecho
250 阅读
Java
School
ML
Other
Share
Explore
运维
登录
Search
bbchen
累计撰写
53
篇文章
累计收到
5
条评论
首页
栏目
Java
School
ML
Other
Share
Explore
运维
页面
统计
友链
关于
搜索到
31
篇与
的结果
2023-05-22
JVM_4.类加载与字节码
类的文件结构root@Tencent-Debian:~# cat HelloWorld.java public class HelloWorld { public static void main(String[] args){ System.out.println("Hello World!"); } }root@Tencent-Debian:~# od -t xC HelloWorld.class 0000000 ca fe ba be 00 00 00 37 00 1f 0a 00 06 00 11 09 0000020 00 12 00 13 08 00 14 0a 00 15 00 16 07 00 17 07 0000040 00 18 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 0000060 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e 0000100 75 6d 62 65 72 54 61 62 6c 65 01 00 04 6d 61 69 0000120 6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 0000140 2f 53 74 72 69 6e 67 3b 29 56 01 00 10 4d 65 74 0000160 68 6f 64 50 61 72 61 6d 65 74 65 72 73 01 00 04 0000200 61 72 67 73 01 00 0a 53 6f 75 72 63 65 46 69 6c 0000220 65 01 00 0f 48 65 6c 6c 6f 57 6f 72 6c 64 2e 6a 0000240 61 76 61 0c 00 07 00 08 07 00 19 0c 00 1a 00 1b 0000260 01 00 0c 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 07 0000300 00 1c 0c 00 1d 00 1e 01 00 0a 48 65 6c 6c 6f 57 0000320 6f 72 6c 64 01 00 10 6a 61 76 61 2f 6c 61 6e 67 0000340 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61 2f 6c 0000360 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f 75 74 0000400 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 0000420 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76 61 2f 0000440 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 01 00 0000460 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a 61 76 0000500 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 0000520 00 21 00 05 00 06 00 00 00 00 00 02 00 01 00 07 0000540 00 08 00 01 00 09 00 00 00 1d 00 01 00 01 00 00 0000560 00 05 2a b7 00 01 b1 00 00 00 01 00 0a 00 00 00 0000600 06 00 01 00 00 00 01 00 09 00 0b 00 0c 00 02 00 0000620 09 00 00 00 25 00 02 00 01 00 00 00 09 b2 00 02 0000640 12 03 b6 00 04 b1 00 00 00 01 00 0a 00 00 00 0a 0000660 00 02 00 00 00 03 00 08 00 04 00 0d 00 00 00 05 0000700 01 00 0e 00 00 00 01 00 0f 00 00 00 02 00 10 0000717 根据JVM规范,类文件结构如下:1.1魔数0-3字节,表示它是否是.class类型的文件ca fe ba be1.2版本4-7字节,表示类的版本,如 00 34 (52) 表示是Java 81.3常量池字节码指令...这一块太多,偏于底层,就不细研究了,直接跳到类加载类加载1. 加载将类的字节码载入方法区中,内部采用C++的instanceKlass描述java类, 它的重要field有:_java_mirror 即java的类镜像,例如对String来说,就是String.class,作用是把klass暴露给java使用_super 即父类_fields 即成员变量_methods 即方法_constants 即常量池_class_loader 即类加载器_vtable 虚方法表_itable 接口方法表如果这个类还有父类没有加载,先加载父类加载和链接可能是交替运行的2. 链接2.1 验证验证类是否符合JVM规范,安全性检查2.2 准备为static变量分配空间,设置默认值静态变量存储在堆中分配空间和赋值是两个阶段,分配空间是在准备阶段,而赋值是在初始化阶段如果static变量是final的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶段完成如果static变量是final 的,但属于引用类型,那么赋值也会在初始化阶段完成2.3 解析将常量池中的符号引用解析为直接引用2.4 初始化初始化即调用()v,虚拟机会保证这个类的【构造方法】的线程安全3.类加载器名称加载的类说明Bootstrap ClassLoaderJAVA_HOME/jre/lib无法直接访问Extension ClassLoaderJAVA_HOME/jre/lib/ext上级为Bootstrap,显示为nullApplication ClassLoaderclasspath上级为Extension自定义类加载器自定义上级为Application
2023年05月22日
62 阅读
0 评论
0 点赞
2023-05-18
JVM_3.垃圾回收
1.如何判断对象可以回收1.1 引用计数法Reference Counting记录每个对象被引用的次数,当对象被引用时,引用计数器加1,当引用被释放时,引用计数器减1。当引用计数器为0时,说明该对象没有任何引用,可以被回收。引用计数法的优点是实现简单,回收对象的时间可以很快。但是,引用计数法也存在一些问题,例如无法处理循环引用的情况。如果两个对象相互引用,它们的引用计数器会一直不为0,因此这两个对象将永远不会被回收,从而导致内存泄漏。Java虚拟机(JVM)没有使用引用计数算法来进行垃圾回收。1.2 可达性分析算法它的基本思想是通过一系列称为“根集”(Root Set)的对象作为起点,递归地遍历对象图,标记所有可达的对象,然后清除所有不可达的对象。可以使用Eclipse Memory Analyzer Tool(MAT)来查看分析Java堆转储文件中的内存使用情况和对象引用关系1.3 五种引用强引用:强引用是最为常见的引用类型,它是指通过一个普通的Java对象变量引用另一个Java对象。只要强引用存在,垃圾回收器就不会回收被引用的对象。只有所有GC Roots对象都不通过[强引用]引用该对象,该对象才能被垃圾回收Object obj = new Object();软引用:软引用是一种比强引用弱一些的引用类型,它可以让对象存活一段时间,直到系统内存不足时才会回收。当JVM需要回收内存时,会先回收所有的弱引用对象,然后再回收所有的软引用对象。软引用可以通过java.lang.ref.SoftReference类来实现。仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象可以配合引用队列来释放软引用自身Object obj = new Object(); SoftReference<Object> softRef = new SoftReference<>(obj);弱引用:弱引用是一种比软引用更加弱的引用类型,它可以让对象存活直到没有任何强引用指向它为止,即只有弱引用指向它时垃圾回收器会回收它。弱引用可以通过java.lang.ref.WeakReference类来实现。仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象,可以配合引用队列来释放弱引用自身Object obj = new Object(); WeakReference<Object> weakRef = new WeakReference<>(obj);虚引用:虚引用是一种最弱的引用类型,它几乎没有引用价值,主要用于在对象被回收之前,做一些必要的清理工作。当垃圾回收器决定回收一个对象时,如果它存在虚引用,就会在回收对象之前将虚引用加入到一个队列中,程序可以通过这个队列来获取对象被回收的通知。虚引用可以通过java.lang.ref.PhantomReference类来实现。必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存Object obj = new Object(); PhantomReference<Object> phantomRef = new PhantomReference<>(obj, referenceQueue);终结器引用:终结器引用是一种特殊的引用类型,它与对象的终结器相关联。在Java中,对象的终结器是一个方法,当垃圾回收器准备回收对象时,会先调用对象的终结器方法。终结器引用可以通过java.lang.ref.FinalizerReference类来实现,但是由于终结器引用的实现过于复杂,因此在Java 9中已经被弃用。无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由Finalizer线程通过终结器弓|用找到被弓|用对象并调用它的finalize方法,第二次GC时才能回收被引用对象2.垃圾回收算法2.1 标记清除算法清除时,并不需要将垃圾内存置零,而是记录下起始和结束位置,以提供给接下来的程序使用。然而,这样的做法可能会导致内存空间的碎片化,从而影响程序的性能。2.2 标记整理算法标记-整理算法则会先标记出所有需要回收的对象,然后将所有存活的对象向一端移动,最后清理掉不需要的对象。这个过程中,整理出来的存活对象会被移动到一端,释放出来的空闲内存空间会被整理成一块连续的内存区域,以便后续程序使用。这样的做法可以减少内存空间的碎片化,提高程序的性能。2.3 复制算法复制算法是一种基于空闲列表的垃圾回收算法,它将堆空间分成两块大小相同的区域,每次只使用其中一块,称为“From Space”,另一块则保持空闲,称为“To Space”。当From Space中的对象需要进行垃圾回收时,复制算法会将其中存活的对象复制到To Space中,并将From Space中的所有对象全部清除。这样一来,To Space中就会有一块连续的内存空间,可以供程序使用。需要注意的是,Java虚拟机的垃圾回收算法是动态选择的,根据垃圾回收器的实现和运行时情况来选择合适的算法。因此,在实际应用中,可能会使用不同的垃圾回收器和不同的垃圾回收算法,以达到最优的垃圾回收效果。3.分代垃圾回收JVM中的内存被分为不同的区域,其中包括新生代和老年代。新生代是JVM内存的一部分,它是用于存放新创建的对象的区域。当创建一个新的对象时,JVM将其分配到新生代中的Eden空间。如果Eden空间没有足够的空间来存放新对象,JVM将会启动垃圾回收机制,将不再被引用的对象清理出内存。如果存活下来的对象足够多,它们将会被移动到新生代中的Survivor空间。经过多次回收后仍然存活下来的对象会被晋升到老年代中。老年代是JVM内存的另一部分,它是用于存放已经存活了一段时间的对象的区域。当一个对象在新生代经历了多次垃圾回收仍然存活下来,它将被移动到老年代中。老年代是相对稳定的,垃圾回收的频率比新生代要低。对象首先分配在Eden(伊甸园)区域新生代空间不足时,触发 minor gc,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄+1且交换 from tominor gc 会引发 stop the world,即暂停其他用户的线程,待垃圾回收结束,用户线程才恢复运行当对象寿命超过阈值(最大是15->4bit)时,会晋升到老年代当需要分配一个大对象时,JVM会先检查老年代的剩余空间是否足够,如果足够,则将该对象直接分配到老年代中。当老年代空间不足,会先尝试触发minor gc,如果之后空间仍然不足,那么触发 full gc,STW(stop the world)的时间更长相关VM参数参数含义-Xms堆初始大小-Xmx 或 -XX:MaxHeapSize=size堆最大大小-Xmn 或 -XX:NewSize=size + -XX:MaxNewSize=size新生代大小-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptivePolicy幸存区比例(动态)-XX:SurvivorRatio=ratio幸存区比例-XX:MaxTenuringThreshold=threshold晋升阈值-XX:+PrintTenuringDistribution晋升详细-XX:+PrintGCDetails -verbose:gcGC详细-XX:+ScavengeBeforeFullGCFullGC 前 MinorGC4. 垃圾回收器串行单线程堆内存较小,适合个人电脑吞吐量优先多线程堆内存较大,多核CPU使得单位时间内的STW的时间最短响应时间优先多线程对此内存较大,多核CPU尽可能缩短单次STW时间4.1 串行-XX:+UseSerialGC=Serial + SerialOldSerial->复制算法;SerialOld->标记整理算法4.2 吞吐量优先-XX:+UseParallelGC ~ -XX:+UseParallerOldGC-XX:+UseAdapticeSizePolicy(动态调整)-XX:GCTimeRatio=ratio(1/1+ratio -> 垃圾回收占总时间的比例)-XX:MaxGCPauseMillis=ms-XX:ParallelGCThreads=n(线程数)4.3 响应时间优先-XX:+UseConCMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads-XX:CMSInitiatingOccupancyFraction=percent(老年代堆空间使用率的百分比阈值,默认是68%)-XX:+CMSScavengeBeforeRemark4.4 G1定义: Garbage First2004论文发布2009 JDK 6u14体验2012 JDK 7u4官方支持2017JDK9默认适翻场景同时注重吞吐量(Throughput) 和低延迟(Low latency),默认的暂停目标是200 ms超大堆内存,会将堆划分为多个大小相等的Region整体上是标记+整理算法,两个区域之间是复制算法相关JVM参数-XX:+UseG1GC-XX:G1HeapRegionSize=size-XX:MaxGCPauseMillis=time1)G1垃圾回收阶段3)Young Collection + CM在Young GC 时会进行 GC Root的初始标记老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),由下面的JVM参数决定:-XX:InitiatingHeapOccupancyPercent=percent (默认45%)4)Mixed GC会对E、S、O进行全面垃圾回收最终标记(Remark)会STW拷贝存活(Evacuation)会STW-XX:MaxGCPauseMillis=ms优先收集较大的老年代内存区5)Full GCSerialGC新生代内存不足发生的垃圾回收 - minor gc老生代内存不足发生的垃圾回收 - full gcParallelGC新生代内存不足发生的垃圾回收 - minor gc老生代内存不足发生的垃圾回收 - full gcCMS新生代内存不足发生的垃圾回收 - minor gc老生代内存不足发生的垃圾回收并发收集失败 -> full gcG1新生代内存不足发生的垃圾回收 - minor gc老生代内存不足发生的垃圾回收新生代内存不足 -> Mixed GC老生代内存不足时 -> full gc6)Young Collection 跨代引用新生代回收的跨代引用(老年代引用新生代)问题卡表与Remembered Set在引用变更时通过post-write barrier + dirty card queueconcurrent refinement threads更新Remembered Set7)Remarkpre-write barrier + satb_mark_queue8)JDK 8u20 字符串去重优点:节省大量内存.缺点:略微多占用了cpu时间,新生代回收时间略微增加-XX: +UseStringDeduplicationString s1 = new String("hello"); // char[]{'h','e','l','1','o'} String s2 = new String("hello"); // char[]{'h','e','1','l','o'}将所有新分配的字符串放入一个队列当新生代回收时,G1并发检查是否有字符串重复如果它们值-样,让它们引用同一个char[]|注意,与String. intern()不一样String. intern()关注的是字符串对象而字符串去重关注的是char[]在JVM内部,使用了不同的字符串表9)JDK 8u40并发标记类卸载所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类条件包括:该类所有的实例都已被回收。该类的 ClassLoader 已经被回收。该类没有在其他地方被引用。-XX:+ClassUnloadingWithConcurrentMark (默认启用)10)JDK 8u60回收巨型对象一个对象大于region的一半时,称之为巨型对象G1不会对巨型对象进行拷贝回收时被优先考虑G1会跟踪老年代所有incoming引用,这样老年代incoming引用为0的巨型对象就可以在新生代垃圾回收时处理掉11)JDK 9并发标记起始时间的调整并发标记必须在堆空间占满前完成,否则退化为FullGCJDK 9之前需要使用-XX: InitiatingHeapOccupancyPercentJDK 9可以动态调整-XX: InitiatingHeap0ccupancyPercent用来设置初始值进行数据采样并动态调整总会添加一个安全的空档空间5.垃圾回收调优java -XX:+PrintFlagsFinal -version | findstr "GC"5.1 新生代调优新生代的特点:所有new操作的内存分配非常廉价TLAB thread-local allocation buffer死亡对象的回收代价是0大部分对象用过即死Minor GC 的时间远远低于Full GC调优原则:晋升阈值配置得当,让长时间存活对象尽快晋升-XX:MaxTenuringThreshold=threshold-XX:+PrintTenuringDistribution5.2 老年代调优以CMS为例CMS的老年代内存越大越好先尝试不做调优,如果没有Full GC说明老年代OK,否则先尝试调优新生代观察发生Full GC时老年代的内存占用,将老年代内存预设调大1/4 ~ 1/3-XX:CMSInitiatingOccupancyFraction=percent
2023年05月18日
63 阅读
0 评论
0 点赞
2023-05-13
JVM_2.内存结构
1.程序计数器1.1 定义Program Counter Register 程序计数器(寄存器),是线程私有的,不会存在内存溢出。1.2 作用Java源代码经过编译器编译为字节码,并在JVM上运行,JVM将字节码解释为JVM指令,然后在CPU上执行。程序计数器是JVM的一部分,用于跟踪当前线程下一条要执行的JVM指令的位置。程序计数器在物理上是寄存器。2.虚拟机栈2.1 定义Java Virtual Machine Stacks(Java虚拟机栈)每个线程运行时所需要的内存,称为虚拟机栈每个栈由多个栈帧(Frame)组成,对应着每次方法调用所占用的内存每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法问题辨析垃圾回收是否涉及栈内存?不涉及栈内存。JVM垃圾回收主要针对堆内存进行回收,堆内存是Java程序中用于存储对象的内存区域。在Java中,栈内存用于存储局部变量和方法调用堆栈,它的生命周期与方法调用相关。在方法调用结束时,栈帧会被释放,这些局部变量也随之被销毁。因此,栈内存的管理不需要垃圾回收器介入。栈内存分配的大小越大越好吗?过小的栈内存可能导致栈溢出的问题,而过大的栈内存则会导致资源的浪费。当一个线程的栈空间被分配过大时,可能会占用大量的内存资源,从而减缓程序的运行速度。此外,过大的栈内存还可能导致内存碎片的问题,从而影响程序的性能。方法内的局部变量是否线程安全?方法内的局部变量在多线程环境中是否线程安全取决于局部变量的数据类型和作用域。如果是基本数据类型或者不可变对象,并且作用域仅限于方法内部,那么它们是线程安全的;如果是可变对象或者作用域超出方法内部,那么就需要考虑线程安全问题。2.2 栈内存溢出栈帧过多导致栈内存溢出(报StackOverflowError)package com.bbedu.jvm.t1; /** * @projectName: jvm-study * @package: com.bbedu.jvm.t1 * @className: JavaVMStackSOF * @author: BBChen * @description: 测试栈帧过多导致栈内存溢出 * @date: 2023/5/11 19:53 * @version: 1.0 * VM Args: -Xss256k */ public class JavaVMStackSOF { private int stackLength = 1; public void stackLeak() { stackLength++; stackLeak(); } public static void main(String[] args) { JavaVMStackSOF oom = new JavaVMStackSOF(); try { oom.stackLeak(); } catch (Throwable e) { System.out.println("stack length: " + oom.stackLength); throw e; } } } 实际中,多个类循环引用时,容易出现上面的问题。如何在IDEA中设置虚拟机参数:2.3 线程运行诊断场景一:CPU占用过高定位问题代码:用top定位是哪个进程ps H -eo pid,tid,%cpu | grep 进程id (用ps命令定位是哪个线程引起的CPU占用过高)jstack 进程id(进一步定位到问题代码的源码行号)场景二:迟迟得不到结果同样使用jstack 进程id,查看是否出现死锁。3.本地方法栈Native Method Stacks.例如Object中的clone()、wait()方法等等。4.堆4.1 定义Heap通过new关键字创建的对象都会使用堆内存特点是线程共享的,堆中对象都需要考虑线程安全的问题有垃圾回收机制4.2 堆内存溢出package com.bbedu.jvm.t1; import java.util.ArrayList; /** * @projectName: jvm-study * @package: com.bbedu.jvm.t1 * @className: HeapOOM * @author: BBChen * @description: 测试堆内存溢出异常 * @date: 2023/5/11 20:39 * @version: 1.0 * -Xmx8m */ public class HeapOOM { static class OOMObject { } public static void main(String[] args) { ArrayList<OOMObject> list = new ArrayList<>(); while (true) { list.add(new OOMObject()); } } } java.lang.OutOfMemoryError: Java heap space4.3 堆内存诊断使用jps、jmap、jconsole、jvisualvm等工具。jps查看当前系统中有哪些java进程jmap查看堆内存占用情况 jmap -heap 进程idjconsole、jvisualvm图形化工具5.方法区5.1 定义Method Area. 所有线程共享,存储预加载类的结构(字段、方法、构造器、运行时常量池等等)方法区是在虚拟机启动时被创建,逻辑上是堆的一部分,具体实现不一定。方法区同样会出现OutOfMemoryError,从Java 8开始,方法区已被移除,取而代之的是元空间(MetaSpace)。元空间也会发生OutOfMemoryError异常,原因和方法区类似。5.2 组成5.3 方法区内存溢出1.8之前会出现永久代内存溢出java.lang.OutOfMemoryError: PermGen space -XX:MaxPermSize=8m1.8之后会导致元空间内存溢出java.lang.OutOfMemoryError: Metaspace -XX:MaxMetaspaceSize=10m5.4 运行时常量池常量池,就是一张表,虚拟机指令根据这张表找到要执行的类名、方法名、参数类型、字面量等消息运行时常量池,常量池保存在*.class中,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址5.5 StringTable常量池中的字符串仅是符号,第一-次用到时才变为对象利用串池的机制,来避免重复创建字符串对象字符串变量拼接的原理是StringBuilder (1.8)字符串常量拼接的原理是编译期优化可以使用intern方法,主动将串池中还没有的字符串对象尝试放入串池1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则会把此对象复制一份,再放入串池,会把串池中的对象返回package com.bbedu.jvm.t1; /** * @projectName: jvm-study * @package: com.bbedu.jvm.t1 * @className: StringTableTest * @author: BBChen * @description: 测试StringTable机制 * @date: 2023/5/13 13:20 * @version: 1.0 */ public class StringTableTest { public static void main(String[] args) { String s1 = "a"; String s2 = "b"; String s3 = "a" + "b"; // "ab"存入常量池 String s4 = s1 + s2; // new String("ab") String s5 = "ab"; // 直接从常量池load String s6 = s4.intern(); // 存入常量池,返回常量池中已经存在的"ab" System.out.println(s3 == s4); // false System.out.println(s3 == s5); // true System.out.println(s3 == s6); // true String x2 = new String("c") + new String("d"); // new String("cd"),在堆中 String x1 = "cd"; // "cd" 在常量池中 x2.intern(); // 常量池已经存在,放入失败 // 将上面的最后两行交换,将为true System.out.println(x1 == x2); // false } } 5.5 StringTable位置分为1.7前和1.7后,1.7前StringTable位于堆中永久代(Permanent Generation),1.7后StringTable位于本地内存的元空间(Metaspace)中。可通过OOM错误信息判断。例如,如果错误信息包含“java.lang.OutOfMemoryError: PermGen space”,则表示发生了永久代内存不足错误,而如果错误信息包含“java.lang.OutOfMemoryError: Metaspace”,则表示发生了元空间内存不足错误。5.6 StringTable垃圾回收在进行垃圾回收时,垃圾回收器会扫描内存中的对象,并标记那些仍然被引用的对象。然后,它会清除那些没有被标记为仍然被引用的对象。对于StringTable,垃圾回收器会扫描所有的字符串常量,并标记那些被其他对象引用的字符串,然后清除所有没有被标记为仍然被引用的字符串。这样,不再被引用的字符串就会被垃圾回收器清除,从而释放内存。需要注意的是,由于字符串池中的字符串常量是被多个对象引用的,因此只有当所有对它的引用都被释放时,才能将其清除。因此,在进行垃圾回收时,垃圾回收器需要仔细处理StringTable中的字符串常量,以确保不会意外地清除仍然被引用的字符串。5.7 StringTable性能调优在JVM中,可以通过调整一些参数来优化StringTable的性能,包括以下几个参数:-XX:StringTableSize:用于设置StringTable的大小,即StringTable中桶(Bucket)的数量。默认情况下,StringTableSize的值为1009,即默认有1009个桶。可以根据具体情况调整这个参数的值以提高性能。-XX:+PrintStringTableStatistics:用于打印StringTable的统计信息,例如StringTable中字符串数量、桶的数量、平均链长等等,可以用来评估StringTable的性能。-XX:+PrintFlagsFinal:用于打印JVM中所有的参数及其默认值,可以用来查看StringTableSize以及其他参数的默认值,以帮助确定需要进行哪些调整。还可以使用intern()方法来优化字符串的性能和内存占用。因为字符串池中的字符串是唯一的,可以减少相同内容的字符串在内存中的重复存储。6.直接内存6.1 定义Direct Memory常见于 NIO 操作时,用于数据缓冲区分配回收成本较高,但读写性能高不受 JVM 内存回收管理6.2 分配和回收原理使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用frepMemory 方法ByteBuffer的实现类内部,使用了Cleaner (虚引用) 来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存6.3 禁用显示GC-XX:+DisableExplicitGC,无法回收直接内存,使用时需要注意
2023年05月13日
84 阅读
0 评论
0 点赞
2023-05-13
JVM_1.引言
什么是JVM定义:Java Virtual Machine - Java程序的运行环境(Java二进制字节码的运行环境)好处:一次编写,多处运行自动内存管理,垃圾回收功能数组下标越界检查多态比较:常见的JVM学习路线
2023年05月13日
361 阅读
0 评论
0 点赞
2023-03-23
xml中的转义字符问题及properties中无需转义
在配置mybatis-config.xml的mysql url时,会遇到如下问题:&会报错,这是因为对于xml而言,&和<是非法字符,需要使用转义字符&代替<会产生错误,因为解析器会把该字符解释为新元素的开始。&也会产生错误,因为解析器会把该字符解释为字符实体的开始。当使用外部properties文件时,还有如下问题: <environments default="development"> <environment id="development"> <!--配置数据管理器--> <transactionManager type="JDBC"/> <!--配置数据源--> <dataSource type="POOLED"> <!--配置驱动--> <property name="driver" value="${jdbc.driver}"/> <!--配置连接 mysql url--> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.user}"/> <property name="password" value="${jdbc.pwd}"/> </dataSource> </environment> </environments>jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 jdbc.user=root jdbc.pwd=123456这样配置后,报错如下:org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.sql.SQLNonTransientConnectionException: Cannot load connection class because of underlying exception: com.mysql.cj.exceptions.WrongArgumentException: Malformed database URL, failed to parse the connection string near ';useUnicode=true&characterEncoding=UTF-8'. ### The error may exist in com/bbedu/mapper/MonsterMapper.xml ### The error may involve com.bbedu.mapper.MonsterMapper.getMonsterById ### The error occurred while executing a query ### Cause: java.sql.SQLNonTransientConnectionException: Cannot load connection class because of underlying exception: com.mysql.cj.exceptions.WrongArgumentException: Malformed database URL, failed to parse the connection string near ';useUnicode=true&characterEncoding=UTF-8'.可以看出,在properties中无需转义,转义反而会导致无法解析,因此,properties需要改为:jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 jdbc.user=root jdbc.pwd=123456
2023年03月23日
44 阅读
0 评论
0 点赞
1
2
3
...
7