JVM详解(五)——运行时数据区-方法区
一、概述
1、介绍
《Java虚拟机规范》中明确说明:尽管所有的方法区在逻辑上属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。但对于HotSpot JVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
所以,方法区看作是一块独立于Java堆的内存空间。官方文档:
方法区在JVM启动的时候被创建,并且它的实际的物理内存空间和Java堆区一样,可以是不连续的。方法区的大小和堆一样,可选择固定大小或可扩展。方法区的大小决定了系统可以保持多少个类,如果系统定义了太多的类,导致方法区溢出,会报OOM。比如:加载大量的第三方jar包,Tomcat部署的工程过多(30~50个),大量动态的生成反射类。关闭JVM,会释放这个区域的内存。
2、栈、堆、方法区的交互关系
3、方法区的演进
把方法区理解成接口,永久代和元空间是这个接口的落地实现。在JDK7及以前,习惯上把方法区称为永久代,JDK8开始,使用元空间取代了永久代。二者最大的区别是:元空间不在虚拟机设置的内存中,而是使用本地内存。
本质上,方法区和永久代并不等价,仅是对HotSpot而言的。《Java虚拟机规范》对如何实现方法区,不做统一要求,像BEA JRockit,IBM J9中不存在永久代。
如果方法区无法满足新的内存分配需求时,将报OOM。现在来看,当年使用永久代,不是好的idea,导致Java程序更容易OOM(超过-XX:MaxPermSize上限)。
4、设置方法区大小与OOM
方法区的大小不是固定的,可以动态调整。
JDK7及以前:
-XX:PerSize来设置永久代初始空间大小。默认值是20.75M
-XX:MaxPerSize来设置永久代最大空间大小。32位机器默认是64M,64位机器默认是82M。
当JVM加载的类信息容量超过这个值,会报OOM:PerGen space
JDK8及以后:
元空间的大小可以使用-XX:MetaspaceSize和-XX:MaxMetaspaceSize指定。
Windows下,-XX:MetaspaceSize=约21M,-XX:MaxMetaspaceSize=-1,即没有限制。默认值依赖平台。对于一个64位的服务器端JVM来说,默认-XX:MetaspaceSize=21M。一旦触及这个水位线,Full GC将会触发,并卸载没用的类(即这些类对应的类加载器不再存活)。然后这个水位线将会重置,新的水位线的值取决于GC后释放了多少元空间。如果释放的空间不足,那么在不超过MaxMetaspaceSize时,适当提高该值。如果释放空间过多,则适当降低该值。
如果初始的水位线设置过低,上述水位线调整情况会发生很多次。通过垃圾回收器的日志可以观察到Full GC多次调用。为了避免频繁FC,建议将初始值设置为一个相对较高的值。
代码示例:方法区OOM
1 // jdk6/7中:
2 // -XX:PermSize=10m -XX:MaxPermSize=10m
3
4 // jdk8中:
5 // -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
6 public class OOMTest extends ClassLoader {
7 public static void main(String[] args) {
8 int j = 0;
9 try {
10 OOMTest test = new OOMTest();
11 for (int i = 0; i < 10_000; i++) {
12 // 创建ClassWriter对象,用于生成类的二进制字节码
13 ClassWriter classWriter = new ClassWriter(0);
14 // 指明版本号,修饰符,类名,包名,父类,接口
15 classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
16
17 byte[] code = classWriter.toByteArray();
18
19 // 类的加载
20 // Class对象
21 test.defineClass("Class" + i, code, 0, code.length);
22
23 j++;
24 }
25 } finally {
26 System.out.println(j);
27 }
28 }
29 }
30
31 // 不设置参数
32 // 10000.无报错.使用动态的方法区,元空间.
33 // 在元空间创建并加载了10000个类.元空间做了一个动态的扩展.
34
35 // JDK8设置参数
36 // 8466.
37 // Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
如何解决这些OOM?
(1)要解决OOM异常或heap space的异常,一般的手段是首先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
(2)如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots到引用链。于是就能找到泄漏对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的。掌握了泄漏对象的类型信息,以及GC Roots引用链的信息,就可以比较准确的定位出泄漏代码的位置。
(3)如果不存在内存泄漏,内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(-Xmx,-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长,持有状态时间过长的情况,尝试减少程序运行期的内存消耗。
二、方法区的内部结构
1、介绍
《深入理解Java虚拟机》书中对方法区存储内容描述如下:它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编辑器编译后的代码缓存等。
注意:这里只是一个经典的结构图,不完全正确。因为在jdk7以后,静态变量,运行时常量池里面的字符串常量池有变化,不再放在方法区了,而是放在了堆空间,这个后面会提到(看图)。
2、类型信息
对每个加载的类型(类Class、接口Interface、枚举Enum、注解Annotation),JVM必须在方法区中存储以下类型信息:
(1)这个类型的完整类路径(包名.类名)。
(2)这个类型直接父类的完整类路径。
(3)这个类型的修饰符(public,abstract,final的某个子集)。
(4)这个类型直接接口的一个有序列表。
类型信息里记录了,这个类是使用哪个类加载器加载进来的。
3、域(Field)信息
JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。域的相关信息包括:域名称、域类型、域修饰符(public、private、protected、static、final、volatile、transient的某个子集)。
4、方法信息
JVM必须保存所有方法以下信息,同域信息一样包括声明顺序:
(1)方法名称。
(2)方法的返回类型(或void)。
(3)方法的参数列表。
(4)方法的修饰符(public、private、protected、static、final、synchronized、native、abstract的某个子集)。
(5)方法的字节码、操作数栈、局部变量表及大小(abstract和native方法除外)。
(6)异常表(abstract和native方法除外),每个异常处理的开始位置,结束位置,代码处理在程序计数器中的偏移地址,被捕获的异常类的常量池索引。
代码示例:方法区的内部构成
1 public class Main extends Object implements Comparable<String>, Serializable {
2 //属性
3 public int num = 10;
4 private static String str = "测试方法的内部结构";
5
6 public void test1() {
7 int count = 20;
8 System.out.println("count = " + count);
9 }
10
11 public static int test2(int cal) {
12 int result = 0;
13 try {
14 int value = 30;
15 result = value / cal;
16 } catch (Exception e) {
17 e.printStackTrace();
18 }
19 return result;
20 }
21
22 @Override
23 public int compareTo(String o) {
24 return 0;
25 }
26 }
1 javap -v -p Main.class > temp.txt
2
3 // 解析后的字节码文件.(删掉了部分无关的信息)
4 Classfile /D:/workspace/Java/myweb/target/classes/com/lx/myweb/Main.class
5 Last modified 2020-10-6; size 1670 bytes
6 MD5 checksum 54aed047bf420df2ddb06256a1ed4a33
7 Compiled from "Main.java"
8
9 // 类型信息
10 public class com.lx.myweb.Main extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializable
11 minor version: 0
12 major version: 52
13 flags: ACC_PUBLIC, ACC_SUPER
14
15 // 常量池.#1:符号引用
16 Constant pool:
17 #1 = Methodref #18.#53 // java/lang/Object."<init>":()V
18 ……
19 #85 = Utf8 printStackTrace
20 {
21
22 // 域信息
23 public int num;
24 descriptor: I
25 flags: ACC_PUBLIC
26
27 private static java.lang.String str;
28 descriptor: Ljava/lang/String;
29 flags: ACC_PRIVATE, ACC_STATIC
30
31 // 方法信息(构造器)
32 public com.lx.myweb.Main();
33 descriptor: ()V
34 flags: ACC_PUBLIC
35 Code:
36 stack=2, locals=1, args_size=1
37 0: aload_0
38 1: invokespecial #1 // Method java/lang/Object."<init>":()V
39 4: aload_0
40 5: bipush 10
41 7: putfield #2 // Field num:I
42 10: return
43 LineNumberTable:
44 line 6: 0
45 line 8: 4
46 LocalVariableTable:
47 Start Length Slot Name Signature
48 0 11 0 this Lcom/lx/myweb/Main;
49
50 // 方法信息
51 public void test1();
52 descriptor: ()V
53 flags: ACC_PUBLIC
54 Code:
55 stack=3, locals=2, args_size=1
56 0: bipush 20
57 2: istore_1
58 3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
59 6: new #4 // class java/lang/StringBuilder
60 9: dup
61 10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
62 13: ldc #6 // String count =
63 15: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
64 18: iload_1
65 19: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
66 22: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
67 25: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
68 28: return
69 LineNumberTable:
70 line 12: 0
71 line 13: 3
72 line 14: 28
73 LocalVariableTable:
74 Start Length Slot Name Signature
75 0 29 0 this Lcom/lx/myweb/Main;
76 3 26 1 count I
77
78 // 方法信息
79 public static int test2(int);
80 descriptor: (I)I
81 flags: ACC_PUBLIC, ACC_STATIC
82 Code:
83 stack=2, locals=3, args_size=1
84 0: iconst_0
85 1: istore_1
86 2: bipush 30
87 4: istore_2
88 5: iload_2
89 6: iload_0
90 7: idiv
91 8: istore_1
92 9: goto 17
93 12: astore_2
94 13: aload_2
95 14: invokevirtual #12 // Method java/lang/Exception.printStackTrace:()V
96 17: iload_1
97 18: ireturn
98 Exception table:
99 from to target type
100 2 9 12 Class java/lang/Exception
101 LineNumberTable:
102 line 17: 0
103 line 19: 2
104 line 20: 5
105 line 23: 9
106 line 21: 12
107 line 22: 13
108 line 24: 17
109 LocalVariableTable:
110 Start Length Slot Name Signature
111 5 4 2 value I
112 13 4 2 e Ljava/lang/Exception;
113 0 19 0 cal I
114 2 17 1 result I
115 StackMapTable: number_of_entries = 2
116 frame_type = 255 /* full_frame */
117 offset_delta = 12
118 locals = [ int, int ]
119 stack = [ class java/lang/Exception ]
120 frame_type = 4 /* same */
121 MethodParameters:
122 Name Flags
123 cal
124
125 // 子类方法信息
126 public int compareTo(java.lang.String);
127 descriptor: (Ljava/lang/String;)I
128 flags: ACC_PUBLIC
129 Code:
130 stack=1, locals=2, args_size=2
131 0: iconst_0
132 1: ireturn
133 LineNumberTable:
134 line 29: 0
135 LocalVariableTable:
136 Start Length Slot Name Signature
137 0 2 0 this Lcom/lx/myweb/Main;
138 0 2 1 o Ljava/lang/String;
139 MethodParameters:
140 Name Flags
141 o
142
143 // 父类方法信息
144 public int compareTo(java.lang.Object);
145 descriptor: (Ljava/lang/Object;)I
146 flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
147 Code:
148 stack=2, locals=2, args_size=2
149 0: aload_0
150 1: aload_1
151 2: checkcast #13 // class java/lang/String
152 5: invokevirtual #14 // Method compareTo:(Ljava/lang/String;)I
153 8: ireturn
154 LineNumberTable:
155 line 6: 0
156 LocalVariableTable:
157 Start Length Slot Name Signature
158 0 9 0 this Lcom/lx/myweb/Main;
159 MethodParameters:
160 Name Flags
161 o synthetic
162
163 // 静态变量信息
164 static {};
165 descriptor: ()V
166 flags: ACC_STATIC
167 Code:
168 stack=1, locals=0, args_size=0
169 0: ldc #15 // String 测试方法的内部结构
170 2: putstatic #16 // Field str:Ljava/lang/String;
171 5: return
172 LineNumberTable:
173 line 9: 0
174 }
175 Signature: #50 // Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/io/Serializable;
176 SourceFile: "Main.java"
字节码文件
5、non-final的类变量
静态变量和类关联在一起,随着类的加载而加载,它们成为类数据在逻辑上的一部分。类变量被类的所有实例共享,即使没有类实例,也可访问。
代码示例:
1 public class Main {
2 public static void main(String[] args) {
3 Order order = null;
4 order.method();
5
6 System.out.println(order.count);
7 }
8 }
9
10 class Order {
11 public static int count = 1;
12
13 public static void method() {
14 System.out.println("count = " + count);
15 }
16 }
17
18 // IDEA会提示有编译错误.但是直接执行不会报错,不会有空指针.
19 // count = 1
20 // 1
6、final的类变量:全局常量
被声明为 final static 的变量的处理方法不同,每个全局常量在编译的时候就被分配了。
代码示例:
1 public class Main {
2
3 public static int count = 1;
4 public static final int num = 100;
5
6 public static void method() {
7 System.out.println("count = " + count);
8 }
9 }
1 javap -v -p Main.class > temp.txt
2
3 // 解析后的字节码文件.(删掉了部分无关的信息)
4 {
5 public static int count;
6 descriptor: I
7 flags: ACC_PUBLIC, ACC_STATIC
8
9 public static final int num;
10 descriptor: I
11 flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
12 ConstantValue: int 100 // 声明为final的静态变量在编译时就有值了
13 }
7、常量池
方法区,内部包含了运行时常量池;字节码文件,内部包含了常量池。要理解方法区,就需要理解清楚字节码文件,因为加载类的信息都在方法区。要理解方法区的运行时常量池,就需要理解字节码文件的常量池。
一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述信息外,还包含一项信息,就是常量池(constant pool),包括各种字面量和对类型、域、方法的符号引用。
(1)为什么需要常量池?
一个Java源文件中的类、接口,编译后产生一个字节码文件。而Java中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里。换一种方式,可以存到常量池,这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池。
代码示例:
1 public class Main {
2 public static void main(String[] args) {
3 System.out.println("hello world");
4 }
5 }
这里面使用了String、System、PrintStream及Object等结构。这里代码量已经很小了,如果代码多,引用到的结构会更多,就需要常量池了!
(2)常量池中有什么?
常量池内存储的数据类型包括:数量值,字符串值,类引用,字段引用,方法引用。
小结:常量池,可以看做一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型。
8、运行时常量池
运行时常量池是方法区的一部分。
常量池表(Constant Pool)是Class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
JVM为每个已加载的类型(类或接口)都维护一个常量池,池中的数据项像数组项一样,是通过索引访问的。
运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期解析后才能够获得的方法或字段引用,此时不再是常量池中的符号地址了,这里换为真实地址。
运行时常量池,相对于Class文件常量池的另一重要特征是,具备动态性。
运行时常量池类似于传统编程语言中的符号表,但是它所包含的数据却比符号表要更加丰富一些。
字节码文件中的常量池经过类加载器加载放到方法区以后,对应的结构就称为运行时常量池。
三、方法区的使用举例
代码示例:方法区使用举例
1 public class Main {
2 public static void main(String[] args) {
3 int x = 500;
4 int y = 100;
5 int a = x / y;
6 int b = 50;
7 System.out.println(a + b);
8 }
9 }
下面讲解在程序执行的过程中,main方法的指令在一个一个执行的过程当中,程序计数器,虚拟机栈之间的协作关系。由于没有new对象,就不看堆空间的情况了。
四、方法区的演进细节(重要)
1、介绍
首先明确,只有HotSpot才有永久代,BEA JRockit,IBM J9中不存在永久代。方法区如何实现属于虚拟机实现细节,《Java虚拟机规范》并不要求统一。
JDK6及以前、JDK7、JDK8及以后,方法区的变化:
StringTable,字符串常量池。
注意:JDK7,此时的方法区(永久代)还是用的虚拟机的内存,和本地内存还有一个映射的概念。JDK8用的本地内存,不再使用虚拟机的内存。
2、永久代为什么被元空间替换?
官网给的理由是因为 JRockit 没有,为了整合JRockit和Hotspot,所以去掉了。这里也很明确的可以看到,将字符串常量池与静态变量移动到堆中。
http://openjdk.java.net/jeps/122
随着Java8 的到来,HotSpot VM中再也见不到永久代了。但这并不意味着类的元数据信息消失了,这些数据被移到了一个与堆不相连的本地内存区域,这个区域叫元空间。
由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。这项变动是很有必要的,原因:
(1)为永久代设置空间大小是很难确定的。在某些场景下,如果动态加载类过多,容易产生Perm区的OOM。比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。而元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
(2)对永久代进行调优是很困难的。对永久代的 Full GC 需要去判断这个类或者这个常量,不再被使用了,也挺花时间的。所以调优也会变得很困难。
3、StringTable(字符串常量池)为什么要调整位置?
因为永久代的回收效率很低,在Full GC的时候才会触发。而Full GC是老年代的空间不足、永久代不足时才会触发,这就导致StringTable回收效率不高。而开发中会有大量的字符串被创建,回收效率低,将导致永久代内存不足,放到堆里,能及时回收内存。
4、如何证明静态变量存在哪?
代码示例:
1 // jdk7:-Xms200m -Xmx200m -XX:PermSize=300m -XX:MaxPermSize=300m -XX:+PrintGCDetails
2 // jdk8:-Xms200m -Xmx200m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=300m -XX:+PrintGCDetails
3 public class Main {
4 // 100MB
5 private static byte[] arr = new byte[1024 * 1024 * 100];
6
7 public static void main(String[] args) {
8 System.out.println(Main.arr);
9
10 // try {
11 // Thread.sleep(1000_000);
12 // } catch (InterruptedException e) {
13 // e.printStackTrace();
14 // }
15 }
16 }
结果:基于jdk8。jdk6,jdk7没有演示。
结论:jdk6,7,8,静态引用对应的对象实体始终都存在堆空间。
注意:这里指的new的对象实体,始终都在堆空间中。上面(图示)指的变化,是静态变量 arr 这个变量名。
那么如何验证呢?
案例:一个静态属性,一个非静态属性,一个局部变量。这三个变量的对象放在哪? 这三个变量本身放在哪?这是《深入理解Java虚拟机》中的案例,staticObj、instanceObj、localObj存放在哪里?
代码示例:
1 // staticObj、instanceObj、localObj存放在哪里?
2 public class StaticObjTest {
3
4 static class Test {
5 static ObjectHolder staticObj = new ObjectHolder();
6 ObjectHolder instanceObj = new ObjectHolder();
7
8 void foo() {
9 ObjectHolder localObj = new ObjectHolder();
10 System.out.println("done");
11 }
12 }
13
14 private static class ObjectHolder {
15 }
16
17 public static void main(String[] args) {
18 Test test = new StaticObjTest.Test();
19 test.foo();
20 }
21 }
这里需要使用一个工具,JHSDB,这个工具JDK9才有。结论:
这三个地址都在eden区,也就是在堆中。也就证明了这三个对象实体都在堆中。即:只要是new出来的对象实体都在堆中。
五、方法区的垃圾回收
1、介绍
《Java虚拟机规范》对方法区的约束是非常宽松的,提到过可以不要求虚拟机在方法区中实现垃圾收集。事实上,也确实有未实现或未能完整实现方法区类型卸载的收集器存在,如JDK 11时期的ZGC收集器就不支持类卸载。
一般来说,这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。以前Sun公司的Bug列表中,曾出现过的若干个严重的Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。
方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。
2、回收内容
方法区常量池中主要存放的两大类常量:字面量和符号引用。
字面量比较接近Java语言层次的常量概念,如文本字符串、被声明为final的常量值等。
符号引用则属于编译原理方面的概念,包括下面三类常量:
(1)类和接口的全限定名
(2)字段的名称和描述符
(3)方法的名称和描述符
HotSpot虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收。回收废弃常量与回收Java堆中的对象非常类似。
3、判定需要回收
4、小结
方法区要不要垃圾回收?Java虚拟机规范中没有明说,可以回收,也可以不回收。平时说的HotSpot是要的。
回收的话,主要针对的是什么?不再使用的类型信息,运行时常量池中废弃的常量。
动态链接,指向了运行时常量池当前方法的引用。
JVM详解(五)——运行时数据区-方法区的更多相关文章
- JVM详解之:运行时常量池
目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ...
- JVM 专题十:运行时数据区(五)堆
1. 核心概述 1.1 堆概述 一个进程对应一个jvm实例,一个运行时数据区,又包含多个线程,这些线程共享了方法区和堆,每个线程包含了程序计数器.本地方法栈和虚拟机栈. 一个jvm实例只存在一个堆内存 ...
- 【JVM第三篇--运行时数据区】程序计数器、虚拟机栈、本地方法栈
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.运行时数据区 我们在编写Java程序时,使用JVM的流程主要如下所示: 虚拟机在 ...
- Mysql常用show命令,show variables like xxx 详解,mysql运行时参数
MySQL中有很多的基本命令,show命令也是其中之一,在很多使用者中对show命令的使用还容易产生混淆,本文汇集了show命令的众多用法. 详细: http://dev.mysql.com/doc/ ...
- JVM——内存区域:运行时数据区域详解
关注微信公众号:CodingTechWork,一起学习进步. 引言 我们经常会被问到一个问题是Java和C++有何区别?我们除了能回答一个是面向对象.一个是面向过程编程以外,我们还会从底层内存管理 ...
- JVM系列之四:运行时数据区
1. JVM架构图 Java虚拟机主要分为五大模块:类装载器子系统.运行时数据区.执行引擎.本地方法接口和垃圾收集模块. 2. JDK1.7内存模型-运行时数据区域 根据<Java 虚拟机规范( ...
- 【JVM第四篇--运行时数据区】堆
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记.其观看地址如下:尚硅谷2020最新版宋红康JVM教程 一.堆的概述 JVM的运行时数据区如下: 一个Java程序运行起来对应着一个进程(操 ...
- Jvm基础(1)-Java运行时数据区
最近在看<深入理解Java虚拟机>,里面讲到了Java运行时数据区,这是Jvm基本知识,把读书笔记记录在此.这些知识属于常识,都能查到的,如果我有理解不对的地方,还请指出. 首先把图贴上来 ...
- JVM内存区域(运行时数据区)划分
前言: 我们每天都在编写Java代码,编译,执行.很多人已经知道Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文 ...
- JVM 专题十三:运行时数据区(八)直接内存
1. 直接内存 不是虚拟机运行时数据区的一部分,也不是<Java虚拟机规范>中定义的内存区域. 直接内存是Java堆外的.直接向系统申请的内存区间. 来源于NIO,通过存在堆中的Direc ...
随机推荐
- JS中原型与原型链
一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object .Function等 是 JS 自带的函数对象.下面举例说明. var o1 ...
- VS dll 引用依赖
在公司实习过程中,经常遇到三个问题: 开发环境 dll引用依赖 dll版本控制 一般公司都会配置开发/测试/Lab/线上四个环境,之后不管时开发什么项目,都与环境分不开边.这个和dll版本控制暂且记下 ...
- Docker - 解决 docker push 上传镜像报:denied: requested access to the resource is denied 的问题
问题背景 在 Linux 已登录自己的 Docker hub 账号 上传本地镜像但是报错了 docker push tomcat 解决方案 docker tag tomcat poloyy/tomca ...
- Nginx:常用基本命令与异常处理
Nginx日志 - ./nginx-1.6.0-ems/logs/nginx.pid Nginx启动时应该使用cmd等命令行工具启动,双击启动同样会产生进程但会造成异常,判断条件是 ./nginx-1 ...
- 逐条更新数据 sql
declare @tid int declare @fid int declare @i int declare @j int set @j=(select count(*) from ...
- NOIP初赛:完善程序做题技巧
最近写的文章好像还很多的.那么今天我们来讨论NOIP初赛的题型--完善程序.完善程序相对是比较难的题目了.全卷100分,完善程序占了大概26分,占比非常大.如果和英语考试试卷做比较,相当于首字母填空( ...
- CDI Features inJavaEE 的上下文和依赖注入
基本的CDI的功能: 类型安全:CDI使用Java类型来解析注入,而不是通过(字符串)名称注入对象.当类型不足时, 可以使用限定符 注释.这允许编译器轻松检测错误,并提供简单的重构. POJO:几乎每 ...
- html阴影 box-shadow
右下阴影 div { box-shadow: 10px 10px 5px #888888; }四周阴影 div { box-shadow: 0 0 5px #888888; } div {box-sh ...
- modern php enable zend opcache
字节码缓存能存储预先编译好的php代码 * 如果是自己编译PHP ./configure --enable-opcache 编译好后 php.ini zend_extension=opcache.so ...
- 鸿蒙内核源码分析(进程镜像篇)|ELF是如何被加载运行的? | 百篇博客分析OpenHarmony源码 | v56.01
百篇博客系列篇.本篇为: v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...