浅析 Java JVM

Posted by 陈谭军 on Sunday, August 18, 2019 | 阅读 |,阅读约 9 分钟

俗话说得好,工欲善其事必先利其器,对于Java开发者来说,简单了解Java虚拟机某些特性有益于提升开发者的内功。为什么我们在编写Java代码前,需要安装并且配置好JDK?JDK内置的这些包功能为什么这么强大?为什么我们的程序会出现内存泄露呢?为什么在代码中加入Synchronized关键字多线程访问程序不会出现数据不一致问题?好多类似的问题,在我们了解JVM之后就会得到答案。要熟悉 Java JVM,大家普遍推荐《深入理解Java虚拟机》周志明著这本书。

总结 Java JVM 可以归纳为以下几个要点:

  1. 跨平台性:Java JVM是Java语言跨平台的关键技术之一。Java源代码首先被编译成字节码(.class文件),然后在JVM上运行。由于JVM本身是针对不同操作系统和硬件架构进行了优化,所以Java应用程序可以在任何装有对应JVM的平台上运行,无需修改源代码。
  2. 即时编译(Just-In-Time Compilation,JIT):JVM在运行时使用即时编译器,将字节码转换为本地机器码。这种编译方式在运行时根据代码的执行情况进行优化,可以提高Java应用程序的执行效率。
  3. 内存管理:JVM负责Java应用程序的内存管理。它通过垃圾回收器(Garbage Collector)自动管理内存,使开发人员不必手动分配和释放内存。垃圾回收器会自动回收不再使用的对象,防止内存泄漏。
  4. 安全性:JVM提供了安全管理器(Security Manager),用于控制Java应用程序的访问权限。通过安全管理器,可以限制应用程序对系统资源的访问,从而提供了一个安全的执行环境。
  5. 多线程支持:JVM允许Java应用程序使用多线程来实现并发处理。JVM负责线程的创建、同步和销毁,使得开发人员能够更方便地编写多线程程序。
  6. 性能监控和调优:JVM提供了丰富的性能监控和调优工具,帮助开发人员识别和解决性能瓶颈。通过这些工具,可以监控内存使用情况、线程状态、垃圾回收等信息,从而优化Java应用程序的性能。

JVM 主要组成部分:

我们首先看段代码,如下所示:

public class happenBeforeDemo {
    private int value = 0;
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
    public static void main(String[] args) {
        happenBeforeDemo happenBeforeDemo = new happenBeforeDemo();
        new Thread(() -> happenBeforeDemo.setValue(22)).start();
        new Thread(() -> System.out.println(happenBeforeDemo.getValue())).start();
    }
}

大家猜测下输出的结果是什么?0 还是 1?而这段代码遵循的是什么原则?先行并发原则是什么?
衡量线程安全性问题 不要受时间顺序的干扰 而是一切以先行发生原则为基准
程序次序规则 管程锁定规则 volatile变量规则 线程启动规则 线程终止规则 线程中断规则 对象终结规则 传递性

由于第一个线程和第二个线程分别调用 setValue() 和 getValue() 两个函数,不在同一个线程中,程序次序规则不适用,由于没有同步块则自然不会发生 lock 操作与 unlock 操作,线程锁定不适合。由于 value 没有添加 volatile 修饰,自然就没有 volatile 变量规则,后面的线程启动,线程终止,线程中断,对象终结规则也和这里没有关系。最后一条传递性也不适用。所以该操作为线程不安全操作。解决方案也比较常见,使用 synchronized 与 volatile 保证上述原则。我们知道这段代码最终会编译成 .class 文件,在我们查看字节码之前,先了解下以下简单的知识。字节码的格式说明:

  1. 魔术与 Class 文件的版本
  2. 常量池
  3. 访问标志
  4. 类索引父索引与接口索引集合
  5. 字段表集合
  6. 方法表集合
  7. 属性表集合(1code 2Exceptions 3 LineNumberTable 4LocalVariableTable 5 SourceFile属性 6ConstantValue属性 7 InnerClasses)
  8. Deprecated 以及 Synthetic 属性
  9. StackMapTable 属性(JDK1.6 后加的)
  10. Signature 属性(JDK1.5 后加的)
  11. BootstrapMethods 属性(JDK1.7 后加的)

Java 版本与符号列表如下所示:

版本符号
Java 1.20x002E=46
Java 1.30x002F=47
Java 1.40x0030=48
Java 50x0031=49
Java 60x0032=50
Java 70x0033=51
Java 80x0034=52

常量池 00 81 128 2个字节,从1开始,0另有所用,u2类型也就是2个字节,最大值 65535。Java 中如果出现超过 64KB 英文字符的遍历或者方法名,将会无法编译,访问标志:2个字节 0A 00。

CONSTANT_Methodref_info {u1 tag; u2 class_index;u2 name_and_type_index;}
Tag 10    class_index   30

CONSTANT_Utf8_info 示例说明:

这样常量池的 21 个常量全部分析完毕,不过 JDK 提供了一个方便的工具可以让我们查看常量池中所包含的常量。通过 javap -verbose TestClass 即可得到所有常量池中的常量,如下所示:

Classfile /F:/AllLearnDemo/target/classes/ctj/jvm/VolatileDemo.class
  Last modified Apr 28, 2019; size 2191 bytes
  MD5 checksum 4b8bdbdb19c2d903e032c4216643bcd5
  Compiled from "VolatileDemo.java"
public class ctj.jvm.VolatileDemo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER(解释:ACC_PUBLIC大家好理解  ACC_SUPER是jdk1.0.2之后编译的类都会带有的标志)
Constant pool:
    #1 = Methodref          #30.#66       // java/lang/Object."<init>":()V
    #2 = Class              #67           // java/lang/Thread
    #3 = Class              #68           // ctj/jvm/VolatileDemo$1
    #4 = Methodref          #3.#66        // ctj/jvm/VolatileDemo$1."<init>":()V
    #5 = Methodref          #2.#69        // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
    #6 = Methodref          #2.#70        // java/lang/Thread.start:()V
    #7 = Fieldref           #71.#72       // java/lang/System.out:Ljava/io/PrintStream;
    #8 = Methodref          #2.#73        // java/lang/Thread.getName:()Ljava/lang/String;
    #9 = Methodref          #74.#75       // java/io/PrintStream.println:(Ljava/lang/String;)V
   #10 = Methodref          #2.#76        // java/lang/Thread.activeCount:()I
   #11 = Methodref          #2.#77        // java/lang/Thread.yield:()V
   #12 = Methodref          #78.#79       // java/lang/management/ManagementFactory.getThreadMXBean:()Ljava/lang/management/ThreadMXBean;
   #13 = InterfaceMethodref #80.#81       // java/lang/management/ThreadMXBean.dumpAllThreads:(ZZ)[Ljava/lang/management/ThreadInfo;
   #14 = Class              #82           // java/lang/StringBuilder
   #15 = Methodref          #14.#66       // java/lang/StringBuilder."<init>":()V
   #16 = String             #83           // [
   #17 = Methodref          #14.#84       // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #18 = Methodref          #85.#86       // java/lang/management/ThreadInfo.getThreadId:()J
   #19 = Methodref          #14.#87       // java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
   #20 = String             #88           // ]
   #21 = Methodref          #85.#89       // java/lang/management/ThreadInfo.getThreadName:()Ljava/lang/String;
   #22 = Methodref          #14.#90       // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #23 = Fieldref           #29.#91       // ctj/jvm/VolatileDemo.raceB:Ljava/util/concurrent/atomic/AtomicInteger;
   #24 = Methodref          #74.#92       // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #25 = Fieldref           #29.#93       // ctj/jvm/VolatileDemo.race:I
   #26 = Methodref          #27.#94       // java/util/concurrent/atomic/AtomicInteger.incrementAndGet:()I
   #27 = Class              #95           // java/util/concurrent/atomic/AtomicInteger
   #28 = Methodref          #27.#96       // java/util/concurrent/atomic/AtomicInteger."<init>":(I)V
   #29 = Class              #97           // ctj/jvm/VolatileDemo
   #30 = Class              #98           // java/lang/Object
   #31 = Utf8               InnerClasses
   #32 = Utf8               race
   #33 = Utf8               I
   #34 = Utf8               raceB
   #35 = Utf8               Ljava/util/concurrent/atomic/AtomicInteger;
   #36 = Utf8               <init>
   #37 = Utf8               ()V
   #38 = Utf8               Code
   #39 = Utf8               LineNumberTable
   #40 = Utf8               LocalVariableTable
   #41 = Utf8               this
   #42 = Utf8               Lctj/jvm/VolatileDemo;
   #43 = Utf8               main
   #44 = Utf8               ([Ljava/lang/String;)V
   #45 = Utf8               i
   #46 = Utf8               info
   #47 = Utf8               Ljava/lang/management/ThreadInfo;
   #48 = Utf8               args
   #49 = Utf8               [Ljava/lang/String;
   #50 = Utf8               threads
   #51 = Utf8               [Ljava/lang/Thread;
   #52 = Utf8               threadMXBean
   #53 = Utf8               Ljava/lang/management/ThreadMXBean;
   #54 = Utf8               threadInfo
   #55 = Utf8               [Ljava/lang/management/ThreadInfo;
   #56 = Utf8               StackMapTable
   #57 = Class              #51           // "[Ljava/lang/Thread;"
   #58 = Class              #49           // "[Ljava/lang/String;"
   #59 = Class              #99           // java/lang/management/ThreadMXBean
   #60 = Class              #55           // "[Ljava/lang/management/ThreadInfo;"
   #61 = Utf8               increase
   #62 = Utf8               increaseB
   #63 = Utf8               <clinit>
   #64 = Utf8               SourceFile
   #65 = Utf8               VolatileDemo.java
   #66 = NameAndType        #36:#37       // "<init>":()V
   #67 = Utf8               java/lang/Thread
   #68 = Utf8               ctj/jvm/VolatileDemo$1
   #69 = NameAndType        #36:#100      // "<init>":(Ljava/lang/Runnable;)V
   #70 = NameAndType        #101:#37      // start:()V
   #71 = Class              #102          // java/lang/System
   #72 = NameAndType        #103:#104     // out:Ljava/io/PrintStream;
   #73 = NameAndType        #105:#106     // getName:()Ljava/lang/String;
   #74 = Class              #107          // java/io/PrintStream
   #75 = NameAndType        #108:#109     // println:(Ljava/lang/String;)V
   #76 = NameAndType        #110:#111     // activeCount:()I
   #77 = NameAndType        #112:#37      // yield:()V
   #78 = Class              #113          // java/lang/management/ManagementFactory
   #79 = NameAndType        #114:#115     // getThreadMXBean:()Ljava/lang/management/ThreadMXBean;
   #80 = Class              #99           // java/lang/management/ThreadMXBean
   #81 = NameAndType        #116:#117     // dumpAllThreads:(ZZ)[Ljava/lang/management/ThreadInfo;
   #82 = Utf8               java/lang/StringBuilder
   #83 = Utf8               [
   #84 = NameAndType        #118:#119     // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #85 = Class              #120          // java/lang/management/ThreadInfo
   #86 = NameAndType        #121:#122     // getThreadId:()J
   #87 = NameAndType        #118:#123     // append:(J)Ljava/lang/StringBuilder;
   #88 = Utf8               ]
   #89 = NameAndType        #124:#106     // getThreadName:()Ljava/lang/String;
   #90 = NameAndType        #125:#106     // toString:()Ljava/lang/String;
   #91 = NameAndType        #34:#35       // raceB:Ljava/util/concurrent/atomic/AtomicInteger;
   #92 = NameAndType        #108:#126     // println:(Ljava/lang/Object;)V
   #93 = NameAndType        #32:#33       // race:I
   #94 = NameAndType        #127:#111     // incrementAndGet:()I
   #95 = Utf8               java/util/concurrent/atomic/AtomicInteger
   #96 = NameAndType        #36:#128      // "<init>":(I)V
   #97 = Utf8               ctj/jvm/VolatileDemo
   #98 = Utf8               java/lang/Object
   #99 = Utf8               java/lang/management/ThreadMXBean
  #100 = Utf8               (Ljava/lang/Runnable;)V
  #101 = Utf8               start
  #102 = Utf8               java/lang/System
  #103 = Utf8               out
  #104 = Utf8               Ljava/io/PrintStream;
  #105 = Utf8               getName
  #106 = Utf8               ()Ljava/lang/String;
  #107 = Utf8               java/io/PrintStream
  #108 = Utf8               println
  #109 = Utf8               (Ljava/lang/String;)V
  #110 = Utf8               activeCount
  #111 = Utf8               ()I
  #112 = Utf8               yield
  #113 = Utf8               java/lang/management/ManagementFactory
  #114 = Utf8               getThreadMXBean
  #115 = Utf8               ()Ljava/lang/management/ThreadMXBean;
  #116 = Utf8               dumpAllThreads
  #117 = Utf8               (ZZ)[Ljava/lang/management/ThreadInfo;
  #118 = Utf8               append
  #119 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #120 = Utf8               java/lang/management/ThreadInfo
  #121 = Utf8               getThreadId
  #122 = Utf8               ()J
  #123 = Utf8               (J)Ljava/lang/StringBuilder;
  #124 = Utf8               getThreadName
  #125 = Utf8               toString
  #126 = Utf8               (Ljava/lang/Object;)V
  #127 = Utf8               incrementAndGet
  #128 = Utf8               (I)V
{
  public static volatile int race;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_VOLATILE

  public static java.util.concurrent.atomic.AtomicInteger raceB;
    descriptor: Ljava/util/concurrent/atomic/AtomicInteger;
    flags: ACC_PUBLIC, ACC_STATIC

  public ctj.jvm.VolatileDemo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lctj/jvm/VolatileDemo;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=6, locals=8, args_size=1  (类似汇编写法)
         0: bipush        10
         2: anewarray     #2                  // class java/lang/Thread
         5: astore_1
         6: iconst_0
         7: istore_2
         8: iload_2
         9: aload_1
        10: arraylength
        11: if_icmpge     55
        14: aload_1
        15: iload_2
        16: new           #2                  // class java/lang/Thread
        19: dup
        20: new           #3                  // class ctj/jvm/VolatileDemo$1
        23: dup
        24: invokespecial #4                  // Method ctj/jvm/VolatileDemo$1."<init>":()V
        27: invokespecial #5                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
        30: aastore
        31: aload_1
        32: iload_2
        33: aaload
        34: invokevirtual #6                  // Method java/lang/Thread.start:()V
        37: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        40: aload_1
        41: iload_2
        42: aaload
        43: invokevirtual #8                  // Method java/lang/Thread.getName:()Ljava/lang/String;
        46: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        49: iinc          2, 1
        52: goto          8
        55: invokestatic  #10                 // Method java/lang/Thread.activeCount:()I
        58: iconst_2
        59: if_icmple     68
        62: invokestatic  #11                 // Method java/lang/Thread.yield:()V
        65: goto          55
        68: invokestatic  #12                 // Method java/lang/management/ManagementFactory.getThreadMXBean:()Ljava/lang/management/ThreadMXBean;
        71: astore_2
        72: aload_2
        73: iconst_0
        74: iconst_0
        75: invokeinterface #13,  3           // InterfaceMethod java/lang/management/ThreadMXBean.dumpAllThreads:(ZZ)[Ljava/lang/management/ThreadInfo;
        80: astore_3
        81: aload_3
        82: astore        4
        84: aload         4
        86: arraylength
        87: istore        5
        89: iconst_0
        90: istore        6
        92: iload         6
        94: iload         5
        96: if_icmpge     154
        99: aload         4
       101: iload         6
       103: aaload
       104: astore        7
       106: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       109: new           #14                 // class java/lang/StringBuilder
       112: dup
       113: invokespecial #15                 // Method java/lang/StringBuilder."<init>":()V
       116: ldc           #16                 // String [
       118: invokevirtual #17                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       121: aload         7
       123: invokevirtual #18                 // Method java/lang/management/ThreadInfo.getThreadId:()J
       126: invokevirtual #19                 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
       129: ldc           #20                 // String ]
       131: invokevirtual #17                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       134: aload         7
       136: invokevirtual #21                 // Method java/lang/management/ThreadInfo.getThreadName:()Ljava/lang/String;
       139: invokevirtual #17                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       142: invokevirtual #22                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       145: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       148: iinc          6, 1
       151: goto          92
       154: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       157: getstatic     #23                 // Field raceB:Ljava/util/concurrent/atomic/AtomicInteger;
       160: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
       163: return
      LineNumberTable:
        line 16: 0
        line 17: 6
        line 18: 14
        line 26: 31
        line 27: 37
        line 17: 49
        line 29: 55
        line 30: 62
        line 32: 68
        line 33: 72
        line 34: 81
        line 35: 106
        line 34: 148
        line 37: 154
        line 38: 163
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            8      47     2     i   I
          106      42     7  info   Ljava/lang/management/ThreadInfo;
            0     164     0  args   [Ljava/lang/String;
            6     158     1 threads   [Ljava/lang/Thread;
           72      92     2 threadMXBean   Ljava/lang/management/ThreadMXBean;
           81      83     3 threadInfo   [Ljava/lang/management/ThreadInfo;
      StackMapTable: number_of_entries = 5 (解释:会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用)
        frame_type = 253 /* append */
          offset_delta = 8
          locals = [ class "[Ljava/lang/Thread;", int ]
        frame_type = 250 /* chop */
          offset_delta = 46
        frame_type = 12 /* same */
        frame_type = 255 /* full_frame */
          offset_delta = 23
          locals = [ class "[Ljava/lang/String;", class "[Ljava/lang/Thread;", class java/lang/management/ThreadMXBean, class "[Ljava/lang/management/ThreadInfo;", class "[Ljava/lang/management/ThreadInfo;", int, int ]
          stack = []
        frame_type = 248 /* chop */
          offset_delta = 61

  public static void increase();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #25                 // Field race:I
         3: iconst_1
         4: iadd
         5: putstatic     #25                 // Field race:I
         8: return
      LineNumberTable:
        line 45: 0
        line 46: 8

  public static void increaseB();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #23                 // Field raceB:Ljava/util/concurrent/atomic/AtomicInteger;
         3: invokevirtual #26                 // Method java/util/concurrent/atomic/AtomicInteger.incrementAndGet:()I
         6: pop
         7: return
      LineNumberTable:
        line 49: 0
        line 50: 7

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=3, locals=0, args_size=0
         0: iconst_0
         1: putstatic     #25                 // Field race:I
         4: new           #27                 // class java/util/concurrent/atomic/AtomicInteger
         7: dup
         8: iconst_0
         9: invokespecial #28                 // Method java/util/concurrent/atomic/AtomicInteger."<init>":(I)V
        12: putstatic     #23                 // Field raceB:Ljava/util/concurrent/atomic/AtomicInteger;
        15: return
      LineNumberTable:
        line 40: 0
        line 42: 4
}
SourceFile: "VolatileDemo.java"
InnerClasses:
     static #3; //class ctj/jvm/VolatileDemo$1

案例一:

public class VolatileDemo {
    /**
     * volatile 保证变量对所有线程的可见性  禁止指令重排序
     * @param args
     */
    public static void main(String[] args) {
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 10000; j++) {
                        increase();
                    }
                }
            });
            threads[i].start();
            System.out.println(threads[i].getName());
        }
        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] threadInfo = threadMXBean.dumpAllThreads(false, false);
        for (ThreadInfo info : threadInfo) {
            System.out.println("[" + info.getThreadId() + "]" + info.getThreadName());
        }
        System.out.println(race);
    }
    public static volatile int race = 0;
    public static void increase() {
        race++;
    } 
}

上述命令中的 System.out.println(race); 的输出结果是什么?结果不确定。

/* Thread-0
* Thread-1
* Thread-2
* Thread-3
* Thread-4
* Thread-5
* Thread-6
* Thread-7
* Thread-8
* Thread-9
* [6]Monitor Ctrl-Break MonitorCtrl-Break 线程 主要功能是 1.Lock Information in Thread Dumps 2.DetectingDeadlocks
* [5]Attach Listener   Attach Listener线程是负责接收到外部的命令而对该命令进行执行的并且吧结果返回给发送者
* [4]SignalDispatcher   Attach Listener当命令接收成功后会交给signaldispather线程去进行分发到各个不同的模块处理命令 并且返回处理结果
* [3]Finalizer 在main线程之后创建的 其优先级为10 主要用于在垃圾收集前调用对象的finalize()方法
* [2]Reference Handler  VM在创建main线程后就创建ReferenceHandler线程 其优先级最高 为10 它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题
* [1]main 主线程
*/

解决方案:

public static volatile int race = 0;
public static void increase() {
    race++;
}

将以上代码替换为以下代码,能够成功解决并发原子性自增问题

public static AtomicInteger raceB = new AtomicInteger(0);
public static void increaseB() {
    raceB.incrementAndGet();
}

案例二:

public class StackOverflowError {
    private static int count;
    public static void count() {
        try {
            count++;
            count();
        } catch (Throwable e) {
            System.out.println("最大深度:" + count);
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        count();
    }
}

public class RuntimeConstantOOM {
    public static void main(String[] args) {
        /**
         * -XX:PermSize=10M -XX:MaxPermSize=10M
         * JDK1.7之前就会抛错
         * JDK1.7之后循环一直进行下去 JDK1.8已经不存在上述配置
         */
        String str = new StringBuilder("啊啊").append("哈哈").toString();
        System.out.println(str.intern() == str);
        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern() == str2);
    }
}

public class HeapOutMemory {
    public static void main(String[] args) {
        // -Xms1m -Xmx10m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
        List<Object> listObject = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            System.out.println("i:" + i);
            Byte[] bytes = new Byte[1 * 1024 * 1024*1024];
            listObject.add(bytes);
        }
        System.out.println("添加成功...");
    }
}

bug 寻找之一起实战故障处理工具。

public void close() {
    if (this.dataSource != null) {
        Pool<Jedis> pool = this.dataSource;
        this.dataSource = null;
        if (this.client.isBroken()) {
            pool.returnBrokenResource(this);
        } else {
            pool.returnResource(this);
        }
    } else {
        super.close();
    }
}
public void close() {
    if (this.dataSource != null) {
        if (this.client.isBroken()) {
            this.dataSource.returnBrokenResource(this);
        } else {
            this.dataSource.returnResource(this);
        }
    } else {
        this.client.close();
    }
}
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.2</version>
</dependency>

比较差异得出释放资源不同。

总的来说,Java JVM 是Java语言的核心运行时环境,为Java应用程序提供了跨平台性、内存管理、多线程支持和安全性等重要功能。JVM的即时编译技术和垃圾回收器使得Java应用程序既能保持高效执行,又能避免手动内存管理的复杂性。因此,JVM在Java生态系统中扮演着至关重要的角色,使得Java成为广泛应用于企业和互联网领域的强大编程语言。