Class类文件结构

class文件一组以8位字节为基础单位的二进制流,每一个字节用2个十六进制表示。在解读这些十六进制数据时,JVM文档定义了一套自己的数据类型来表示class文件数据:使用u1, u2, u4u8分别来代表1个字节2个字节4个字节8个字节。下图使用的是010Editor打开的一个class文件:

Class 文件格式 010Editor

如图所示,u2 minor_version中的u2表示占用2个字节,而上面会显示占用连续的4个十六进制位。在class文件中,字节的存储顺序、长度等都是有严格限定的,不允许改变。

在计算机中,1个字节 = 8位(8个二进制位),1个十六进制 = 4个二进制位 (如:F = 1111),1个字节 = 2个十六进制,所以2个字节用4个十六进制表示。

分析class文件常用的工具:

上图就是使用的010Editor打开的class文件。

Class类文件的结构

Class文件中字节的排列顺序和占用字节大小如下图所示:

类结构文件

其结构如下所示:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

下面对照着结构目录依次解析。

magic

u4 magic 表示magic占用了4个字节存储,而文件中前四个字节是CAFEBABE,表明CAFEBABE属于magic

magic的作用类似于文件的扩展名,我们通过文件扩展名可以很容易看出这个文件是什么类型的文件,但是由于文件扩展名容易被修改,所以在文件中通过magic来标记这个属于什么文件。如:JVM通过读取前4个字节就知道该文件是不是一个class文件。

minor_version 和 major_version

minor_versionmajor_version表示的是class文件被编译时使用的JDK版本号,minor_version表示次版本号,major_version表示主版本号。

常量项个数(constant_pool_count)

The value of the constant_pool_count item is equal to the number of entries in the constant_pool table plus one.

constant_pool_count显示常量池中共有多少个数据项,但实际的数量比constant_pool_count少一个,如constant_pool_count = 72,则constant_pool为[0] ~ [70]。

常量池(constant_pool[constant_pool_count - 1])

The constant_pool table is indexed from 1 to constant_pool_count - 1.

Each item in the constant_pool table must begin with a 1-byte tag indicating the kind of cp_info entry.

constant_pool是一个结构表,它表示在ClassFile结构及其子结构中引用的各种字符串常量,类和接口名称,字段名称以及其他常量。每个常量池结构中的第一个字节必须是tag标签,tag标签表示当前这个常量属于哪种常量类型。

常量池索引是从1到constant_pool_count - 1,而常量池数组是从0开始的,所以索引指向的时候要减1

例如:
name_index = 0x0023 = 35,实际上name索引值指向的是constant_pool[34];
constant_pool_count = 0x0025 = 37,实际常量项个数是36,这样做的目的是满足后面其他结构中需要表明不引用任何一个常量项的含义,这个时候就将索引值置为0。

常量池的结构如下:

cp_info {
    u1 tag;
    u1 info[];
}

tag标签类型有如下14种:

类型标志描述
CONSTANT_Class7类或接口的符号引用
CONSTANT_Fieldref9字段的符号引用
CONSTANT_Methodref10类中方法
CONSTANT_InterfaceMethodref11接口中方法的符号引用
CONSTANT_String8字符串型字面量
CONSTANT_Integer3整形字面量
CONSTANT_Float4浮点型字面量
CONSTANT_Long5长整型字面量
CONSTANT_Double6双精度浮点型字面量
CONSTANT_NameAndType12字段或方法的部分符号引用
CONSTANT_Utf81UTF-8编码的字符串
CONSTANT_MethodHandle15表示方法句柄
CONSTANT_MethodType16表示方法类型
CONSTANT_InvokeDynamic18表示一个动态方法调用点
CONSTANT_Class_info 型常量的结构

CONSTANT_Class_info 型常量代表一个类或接口,其结构如下:

CONSTANT_Class_info {
    u1 tag;			// 用于区分常量类型
    u2 name_index;	// 索引值,指向常量池中一个`CONSTANT_Utf8_info`类型常量,此常量代表了这个类(或者接口)的全限定名
}

除了Object类以外,其他类一般至少有两个CONSTANT_Class_info类型的常量,如下图所示:

CONSTANT_Class_info

在图中u2 name_index的索引值是68,表示的是name_index对应着常量池中constant_pool[67],查看该值:

name_index

由上图可以看出,类名为com/fhmou/base/IntTest

CONSTANT_Utf8_info 型常量的结构

CONSTANT_Utf8_info 型常量用来表示常量字符串值,其结构如下:

CONSTANT_Utf8_info {
    u1 tag;				// 用于区分常量类型
    u2 length;			// length表示字符串长度是多少字节(不是字符串长度)
    u1 bytes[length];	// 字符串字节数组
}

length表示字符串中字节的大小,而u2则表示length最大的取值范围,u2指的是存储2个字节即16位,也就是说,length最大的取值是65535B 65535 B ≈ 64 K B 65535B \approx 64KB 65535B64KB,也就是说Java程序中如果定义了超过64KB英文字符的变量或方法名,将会无法编译。

字段类型存储在CONSTANT_Utf8_info型常量结构中,每种类型的字符串只会在常量池中存储一份,如:

private int a;
private int b;
private int c;

在常量池中存储类型字符串的时候,只会存储一次int,且int在常量池中会改写为I,其他类型标识字符如下所示:

标识字符含义
B基本类型byte
C基本类型char
D基本类型double
F基本类型float
I基本类型int
J基本类型long
L ClassName;对象类型
S基本类型short
Z基本类型boolean
[数组对象
CONSTANT_Fieldref\Methodref\InterfaceMethodref_info 型常量的结构

Fields, methods, and interface methods are represented by similar structures:

CONSTANT_***ref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

class文件是类型的话,常量池中会有一个CONSTANT_Methodref_info类型的常量,如下所示,CONSTANT_Methodref_info的值为java/lang/Object."<init>":()V<init>表示一个实例的初始化方法,放回值为空,这时的CONSTANT_Fieldref_info用于描述字段信息。

#1 = Methodref          #23.#57        // java/lang/Object."<init>":()V
  • tag类型为CONSTANT_Methodref_info时,class_index必须指向一个类,不能是接口;
  • tag类型为CONSTANT_InterfaceMethodref_info时,class_index必须指向一个接口,不能是类;
  • tag类型为CONSTANT_Fieldref_info时,class_index可能指向一个类或接口;name_and_type_index必须是字段描述否则就是方法的描述。

中定义了几个字段,就有几个CONSTANT_Fieldref_info常量,字段在常量池中引用关系如下:

CONSTANT_Fieldref_info

CONSTANT_NameAndType_info型常量的结构

CONSTANT_NameAndType_info型常量结构常用来表示字段方法,但是不包含该字段或方法属于那个类或接口,其结构如下:

CONSTANT_NameAndType_info {
    u1 tag;					// 类型标记
    u2 name_index;			// 指向该字段或方法名称的常量项索引
    u2 descriptor_index;	// 指向字段类型或方法返回值类型的常量项索引
}

如在中定义了一个字段private double dNum = 20.0;,在常量池中可以看到定义的该字段,如下图所示,在CONSTANT_NameAndType_info型常量结构中只显示了name_indexdescriptor_index索引,而不是直接显示值,这是为了减少重复定义。

访问标志(access_flags)

在常量池结束之后,紧接着的两个字节代表访问标志(access_flags),这个标志用来识别这个Class文件是一个还是接口文件,文件的修饰类型:public, final, abstract等,具体的标志含义如下:

Flag十六进制含义
ACC_PUBLIC0x0001定义public类型
ACC_FINAL0x0010定义final类型,不允许有子类
ACC_SUPER0x0020用来表示如何调用父类的方法,JDK 1.0.2以后编译出来的类的这个标志都必须为真
ACC_INTERFACE0x0200标识这是一个接口,不是类
ACC_ABSTRACT0x0400定义abstract类型,不允许实例化
ACC_SYNTHETIC0x1000标识这个类并非由用户代码产生的
ACC_ANNOTATION0x2000标识这是一个注释
ACC_ENUM0x4000标识这是一个枚举类型

Class文件可能有多个访问标志信息,多个访问标志的值加起来的值就是Class文件中标识位的值,如下查看的是一个普通类文件:

如图所示,该类是一个普通的Java类,不是接口、枚举或者注解,被public关键字修饰但没有被申明为final和abstract,并且使用JDK 1.8 编译器编译,所以 access_flags = 0x0001 + 0x0020 = 0x0021。而图中的access_flags标志确为0x0021。

如果定义的是一个接口类,那么访问标志为:ACC_ANNOTATIONACC_INTERFACE

类、父类(this_class、super_class)

this_classsuper_class的值都是一个索引值,指向的是constant_pool数组项。

接口

interfaces_count表示该类文件实现了接口的数量;interfaces[]数组中的每个值都必须是constant_pool表中的有效索引。

字段

fields_count该类或者接口所拥有的字段数。

field_info结构用于表示字段的详细信息,其结构如下:

field_info {
    u2             access_flags;		// 字段访问标志
    u2             name_index;		// 字段名称
    u2             descriptor_index;	// 字段类型
    u2             attributes_count;	// 属性数量
    attribute_info attributes[attributes_count];  // 属性项
}

access_flags:用于表示字段的访问属性,有如下几种类型:

标志名称含义
ACC_PUBLIC字段是否 public
ACC_PRIVATE字段是否 private
ACC_PROTECTED字段是否 protected
ACC_STATIC字段是否 static
ACC_FINAL字段是否 final
ACC_VOLATILE字段是否 volatile
ACC_TRANSIENT字段是否 transient
ACC_SYSTHETIC字段是否由编译器自动生成
ACC_ENUM字段是否 enum

name_index:CONSTANT_Utf8类型常量项的索引,里面存储了字段名称。

descriptor_index:CONSTANT_Utf8类型常量项的索引,里面存储了字段描述符。

描述符标识字符含义:

标识字符含义标识字符含义
BbyteJlong
CcharSshort
DdoubleZboolean
FfloatVvoid
IintL对象类型

注:字段集合中不会列出从超类或者父接口中继承而来的字段。

方法

methods_count: 该类或接口拥有的方法数。

methods:列表每一下为method_info数据,method_info结构如下:

method_info {  
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

access_flags:用于表示方法的访问属性。

name_index:CONSTANT_Utf8类型常量项的索引,里面存储了方法名称。

descriptor_index:CONSTANT_Utf8类型常量项的索引,里面存储了方法描述符。

attributes_count:当前方法拥有的attribute数量。

attributes:列表每一项为attribute_info结构数据,下面会详细描述。

方法中的Java代码,经过编译器编译成字节码指令后,存放在方法属性集合中一个名为Code的属性里面。

属性

Class文件字段方法中都可以携带自己的属性集合,以用于描述某些场景专有信息。

例如:

  • Class文件:SourceFile用于记录类文件名称;
  • 字段:ConstantValue用于表示final关键字定义的常量值;
  • 方法:使用Code属性存放Java代码编译成的字节码指令。
Code属性

每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表操作数栈动态链接方法出口等信息,如下图所示:

Java Stacks

Java程序方法体中的代码经过Javac编译器处理后,最终变为字节码指令存储在Code属性内。并非所有的方法都必须存在这个属性,譬如接口或者抽象类中的方法就不存在Code属性。Code的属性结构如下所示:

Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

attribute_name_index: 指向常量池的索引,常量值固定值为Code

attribute_length: 属性长度,由于属性名称索引与属性长度一共为6个字节,所以属性值的长度固定为整个属性长度减去6个字节。

max_stack: 操作数栈深度的最大值。

max_locals: 代表了局部变量表所需的存储空间。max_locals的单位是SlotSlot是虚拟机为局部变量分配内存所使用的最小单位。

code_lengthcode用来存储Java源程序编译后生成的字节码指令code_length代表字节码长度,code是用来存储字节码指令的一系列字节流。每一个字节码指令就是一个u1类型的字节,而字节码指令的长度不能超过4个字节,所以只要不编写超长的方法,一般字节码指令的个数是不会超过4个字节的。字节码指令如下图所示:

下面列出了几个常用的虚拟机字节码指令表

字节码助记符指令含义
0x2Aaload_0将第一个引用类型本地变量推送至栈顶
0xB7invokespecial调用超类构造方法,实例初始化方法,私有方法
0x11sipush将一个短整型常量值(-32768 ~ 32767)推送至栈顶
0xB5putfield为指定的类的实例域赋值
0x12ldc将int, float 或 String型常量值从常量池中推送至栈顶
0x13ldc_w将int, float 或 String型常量值从常量池中推送至栈顶(宽索引)
0x14ldc2_w将long 或 double型常量值从常量池中推送至栈顶(宽索引)
0xB1return从当前方法返回void
0xB2getstatic获取指定类的静态域,并将其值压入栈顶
OxBBnew创建一个对象,并将其引用值压入栈顶

exception: 作用是列举出方法中可能抛出的异常,也就是方法描述时在throws关键字后面列举的异常。

attributes: 方法的属性,如下图所示:

LineNumberTable属性用于描述Java源码行号与字节码行号之间的对应关系。

LocalVariableTable属性用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。

Javap

在JDK 的bin目录中,Orcale公司为我们提供了一个专门用于分析Class文件字节码的工具:javap,使用javap工具的-verbose参数输出Class文件字节码内容。

源码如下:

package com.fhmou.base;
public class IntTest {

    private int num1 = 1000;
    public final static int num2 = 2000;
    private long lNum1 = 99999;
    private long lNum2 = 99999;
    private double dNum = 20.0;
    private String name1 = "aaaa";
    private String name2 = "aaaa";
    private String text = "bbbb";

    public String getNameTest() {
        return "this is a test";
    }

    public static void main(String[] args) {
        int a = 3;
        int b = 4;
        int c = 4;
        long lNum2 = 200000;
        double dNum2 = 20000.0d;
        String text1 = "aaaa";
        String text2 = "dddd";
    }
}

使用javap反编译:

javap -verbose IntTest
Warning: Binary file IntTest contains com.fhmou.base.IntTest
Classfile /Users/luyanliang/program/me/learnProgram/javaSE/out/production/javaSE/com/fhmou/base/IntTest.class
  Last modified Jul 10, 2019; size 1064 bytes
  MD5 checksum f79a67dd40288c19574373d32c82346a
  Compiled from "IntTest.java"
public class com.fhmou.base.IntTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #22.#58        // java/lang/Object."<init>":()V
   #2 = Fieldref           #21.#59        // com/fhmou/base/IntTest.num1:I
   #3 = Long               99999l
   #5 = Fieldref           #21.#60        // com/fhmou/base/IntTest.lNum1:J
   #6 = Fieldref           #21.#61        // com/fhmou/base/IntTest.lNum2:J
   #7 = Double             20.0d
   #9 = Fieldref           #21.#62        // com/fhmou/base/IntTest.dNum:D
  #10 = String             #63            // aaaa
  #11 = Fieldref           #21.#64        // com/fhmou/base/IntTest.name1:Ljava/lang/String;
  #12 = Fieldref           #21.#65        // com/fhmou/base/IntTest.name2:Ljava/lang/String;
  #13 = String             #66            // bbbb
  #14 = Fieldref           #21.#67        // com/fhmou/base/IntTest.text:Ljava/lang/String;
  #15 = String             #68            // this is a test
  #16 = Long               200000l
  #18 = Double             20000.0d
  #20 = String             #69            // dddd
  #21 = Class              #70            // com/fhmou/base/IntTest
  #22 = Class              #71            // java/lang/Object
  #23 = Utf8               num1
  #24 = Utf8               I
  #25 = Utf8               num2
  #26 = Utf8               ConstantValue
  #27 = Integer            2000
  #28 = Utf8               lNum1
  #29 = Utf8               J
  #30 = Utf8               lNum2
  #31 = Utf8               dNum
  #32 = Utf8               D
  #33 = Utf8               name1
  #34 = Utf8               Ljava/lang/String;
  #35 = Utf8               name2
  #36 = Utf8               text
  #37 = Utf8               <init>
  #38 = Utf8               ()V
  #39 = Utf8               Code
  #40 = Utf8               LineNumberTable
  #41 = Utf8               LocalVariableTable
  #42 = Utf8               this
  #43 = Utf8               Lcom/fhmou/base/IntTest;
  #44 = Utf8               getNameTest
  #45 = Utf8               ()Ljava/lang/String;
  #46 = Utf8               main
  #47 = Utf8               ([Ljava/lang/String;)V
  #48 = Utf8               args
  #49 = Utf8               [Ljava/lang/String;
  #50 = Utf8               a
  #51 = Utf8               b
  #52 = Utf8               c
  #53 = Utf8               dNum2
  #54 = Utf8               text1
  #55 = Utf8               text2
  #56 = Utf8               SourceFile
  #57 = Utf8               IntTest.java
  #58 = NameAndType        #37:#38        // "<init>":()V
  #59 = NameAndType        #23:#24        // num1:I
  #60 = NameAndType        #28:#29        // lNum1:J
  #61 = NameAndType        #30:#29        // lNum2:J
  #62 = NameAndType        #31:#32        // dNum:D
  #63 = Utf8               aaaa
  #64 = NameAndType        #33:#34        // name1:Ljava/lang/String;
  #65 = NameAndType        #35:#34        // name2:Ljava/lang/String;
  #66 = Utf8               bbbb
  #67 = NameAndType        #36:#34        // text:Ljava/lang/String;
  #68 = Utf8               this is a test
  #69 = Utf8               dddd
  #70 = Utf8               com/fhmou/base/IntTest
  #71 = Utf8               java/lang/Object
{
  public static final int num2;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 2000

  public com.fhmou.base.IntTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: sipush        1000
         8: putfield      #2                  // Field num1:I
        11: aload_0
        12: ldc2_w        #3                  // long 99999l
        15: putfield      #5                  // Field lNum1:J
        18: aload_0
        19: ldc2_w        #3                  // long 99999l
        22: putfield      #6                  // Field lNum2:J
        25: aload_0
        26: ldc2_w        #7                  // double 20.0d
        29: putfield      #9                  // Field dNum:D
        32: aload_0
        33: ldc           #10                 // String aaaa
        35: putfield      #11                 // Field name1:Ljava/lang/String;
        38: aload_0
        39: ldc           #10                 // String aaaa
        41: putfield      #12                 // Field name2:Ljava/lang/String;
        44: aload_0
        45: ldc           #13                 // String bbbb
        47: putfield      #14                 // Field text:Ljava/lang/String;
        50: return
      LineNumberTable:
        line 7: 0
        line 9: 4
        line 11: 11
        line 12: 18
        line 13: 25
        line 14: 32
        line 15: 38
        line 16: 44
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      51     0  this   Lcom/fhmou/base/IntTest;

  public java.lang.String getNameTest();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: ldc           #15                 // String this is a test
         2: areturn
      LineNumberTable:
        line 19: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       3     0  this   Lcom/fhmou/base/IntTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=10, args_size=1
         0: iconst_3
         1: istore_1
         2: iconst_4
         3: istore_2
         4: iconst_4
         5: istore_3
         6: ldc2_w        #16                 // long 200000l
         9: lstore        4
        11: ldc2_w        #18                 // double 20000.0d
        14: dstore        6
        16: ldc           #10                 // String aaaa
        18: astore        8
        20: ldc           #20                 // String dddd
        22: astore        9
        24: return
      LineNumberTable:
        line 23: 0
        line 24: 2
        line 25: 4
        line 26: 6
        line 27: 11
        line 28: 16
        line 29: 20
        line 30: 24
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      25     0  args   [Ljava/lang/String;
            2      23     1     a   I
            4      21     2     b   I
            6      19     3     c   I
           11      14     4 lNum2   J
           16       9     6 dNum2   D
           20       5     8 text1   Ljava/lang/String;
           24       1     9 text2   Ljava/lang/String;
}
SourceFile: "IntTest.java"

如上所示,在程序中定义了两个方法:getNameTest()main(String[] args),而不管是使用010Editor还是使用javap查看Class文件都显示三个方法,这是因为默认隐藏了类的构造方法。
在上述三个方法中的args_size都显示1,但是却没有传入参数,这个因为在任何实例方法中,都可以通过this关键字访问到此方法所属的对象。

关于基本数据类型在常量池的存放

int型的值在-32768 ~ 32767之间的话,不会把值放入到常量池中;

long型(包括double, float)的值不是01的话,就会把值放入常量池中。

参考网站

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 的加载、解释、编译,本章节带你深入理解加载器的分、范围、双亲委托策略,自己手写加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用型的使用和区别等等。第7章 GC算法与收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使用,垃圾回收器的组合及分代收集等。第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值