Skip to content

Commit aaf0dd1

Browse files
committed
Update Java Notes
1 parent 153bf27 commit aaf0dd1

1 file changed

Lines changed: 120 additions & 43 deletions

File tree

JavaSE.md

Lines changed: 120 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,7 +1478,7 @@ static 静态修饰的成员(方法和成员变量)属于类本身的。
14781478

14791479
* **堆内存存放对象和变量**
14801480

1481-
* **方法区存放class和静态变量**
1481+
* **方法区存放class**
14821482

14831483
访问问题:
14841484

@@ -2738,7 +2738,7 @@ s.replace("-","");//12378
27382738

27392739
#### String Pool
27402740

2741-
**字符串常量池(String Pool / StringTable / 串池)**保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定
2741+
**字符串常量池(String Pool / StringTable / 串池)**保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定,字符串常量池存储的是字符串对象的引用,而不是字符串本身
27422742

27432743
* StringTable,hashtable (哈希表 + 链表)结构,不能扩容
27442744

@@ -2783,7 +2783,7 @@ public class Demo {
27832783
- == 比较基本数据类型:比较的是具体的值
27842784
- == 比较引用数据类型:比较的是对象地址值
27852785

2786-
Java 7之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7以后,String Pool 被移到堆中,这是因为永久代的空间有限,在大量使用字符串的场景下会导致OutOfMemoryError 错误
2786+
Java 7之前,String Pool 被放在运行时常量池中,它属于永久代Java 7以后,String Pool 被移到堆中,这是因为永久代的空间有限,在大量使用字符串的场景下会导致OutOfMemoryError 错误
27872787

27882788
演示 StringTable 位置:
27892789

@@ -12540,13 +12540,15 @@ JVM 内存结构规定了 Java 在运行过程中内存申请、分配、管理
1254012540

1254112541
Java 虚拟机栈:Java Virtual Machine Stacks,**每个线程**运行时所需要的内存
1254212542

12543-
* 虚拟机栈是**每个线程私有的**,对应方法调用到执行完成的整个过程
12543+
* 每个方法被执行时,都会在虚拟机栈中创建一个栈帧 stack frame(**一个方法一个栈帧**)
12544+
12545+
* 虚拟机栈是**每个线程私有的**,每个线程只能有一个活动栈帧,对应方法调用到执行完成的整个过程
1254412546

1254512547
* 保存执行方法时的**局部变量表、常量池引用、方法返回地址信息**等
1254612548

12547-
* 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
12549+
* 局部变量表中存储着方法里的java基本数据类型以及对象的引用
1254812550

12549-
* 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法(**一个方法一个栈帧**)
12551+
* 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
1255012552

1255112553
<img src="https://gitee.com/seazean/images/raw/master/JavaSE/JVM-虚拟机栈.png" style="zoom:50%;" />
1255212554

@@ -12559,7 +12561,7 @@ Java 虚拟机栈:Java Virtual Machine Stacks,**每个线程**运行时所
1255912561

1256012562
* 栈内存**不需要进行GC**,方法开始执行的时候会进栈,方法调用后自动弹栈,相当于清空了数据
1256112563

12562-
* 栈内存分配越大越大,可用的线程数越少
12564+
* 栈内存分配越大越大,可用的线程数越少(内存越大,每个线程拥有的内存越大)
1256312565

1256412566
* 方法内的局部变量是否**线程安全**:
1256512567
* 如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
@@ -12579,6 +12581,8 @@ Java 虚拟机栈:Java Virtual Machine Stacks,**每个线程**运行时所
1257912581

1258012582

1258112583

12584+
***
12585+
1258212586

1258312587

1258412588
#### 本地方法栈
@@ -12587,7 +12591,9 @@ Java 虚拟机栈:Java Virtual Machine Stacks,**每个线程**运行时所
1258712591

1258812592
* 不需要进行GC,与虚拟机栈类似
1258912593

12590-
* 本地方法一般是由其他语言编写,并且被编译为基于本机硬件和操作系统的程序,对这些方法需要特别处理
12594+
* 虚拟机栈执行的是java方法
12595+
12596+
* 本地方法一般是由其他语言编写,并且被编译为基于本机硬件和操作系统的程序
1259112597

1259212598
* JNI:Java Native Interface,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植
1259312599

@@ -12612,7 +12618,7 @@ Program Counter Register 程序计数器(寄存器)
1261212618

1261312619
特点:
1261412620

12615-
* 是线程私有的
12621+
* **是线程私有的**
1261612622
* 不会存在内存溢出,是JVM规范中唯一一个不出现OOM的区域,所以这个空间不会进行GC
1261712623

1261812624
Java**反编译**指令:`javap -v Test.class`
@@ -12638,12 +12644,16 @@ Java**反编译**指令:`javap -v Test.class`
1263812644

1263912645
#### 堆
1264012646

12641-
Heap 堆:用来保存**对象实例,数组**等,通过 new 创建对象都会使用堆内存,是垃圾收集的主要区域("GC 堆")
12647+
Heap 堆:是JVM内存中最大的一块,由所有线程共享,由垃圾回收器管理的主要区域("GC 堆"),堆中对象都需要考虑线程安全的问题
1264212648

12643-
特点
12649+
存放哪些资源
1264412650

12645-
* 它是线程共享的,堆中对象都需要考虑线程安全的问题
12646-
* 有垃圾回收机制
12651+
* 对象实例:类初始化生成的对象,基本数据类型的数组也是对象实例,new 创建对象都使用堆内存
12652+
* 字符串常量池:
12653+
* 字符串常量池原本存放于方法区,jdk7开始放置于堆中
12654+
* 字符串常量池**存储的是string对象的直接引用,而不是直接存放的对象**,是一张string table
12655+
* 静态变量:静态变量是有static修饰的变量,jdk7时从方法区迁移至堆中
12656+
* 线程分配缓冲区(Thread Local Allocation Buffer):线程私有但不影响堆的共性,可以提升对象分配的效率
1264712657

1264812658
设置堆内存指令:`-Xmx Size`
1264912659

@@ -12683,15 +12693,18 @@ public static void main(String[] args) throws InterruptedException {
1268312693

1268412694
#### 方法区
1268512695

12686-
方法区 / 永久代:在HotSpot JVM中,用于存放**已被加载的类信息**、常量池、静态变量以及即时编译器编译后的代码等数据,每当一个类初次被加载时,它的**元数据**都会放到永久代中
12696+
方法区:是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它也叫 Non-Heap(非堆)
1268712697

1268812698
方法区是一个 JVM 规范,**永久代与元空间都是其一种实现方式**
1268912699

1269012700
方法区和堆一样不需要连续的内存,并且可以动态扩展;方法区有大小限制,因此加载的类太多,可能导致永久代内存溢出 (OutOfMemoryError);对这块区域进行垃圾回收主要是对常量池的回收和对类的卸载,比较难实现
1269112701

12692-
为了**避免方法区出现OOM**,在JDK8中将堆内的方法区(永久代)移动到了本地内存上,重新开辟了一块空间,叫做**元空间**,元空间存储类的元信息,静态变量和常量池等放入堆中
12702+
为了**避免方法区出现OOM**,在JDK8中将堆内的方法区(永久代)移动到了本地内存上,重新开辟了一块空间,叫做**元空间**,元空间存储类的元信息,静态变量和字符串常量池等放入堆中
1269312703

12704+
类元信息:
1269412705

12706+
* 类元信息在类编译期间放入方法区,里面放置了类的基本信息,包括类的方法、参数、接口以及常量池表
12707+
* 常量池表(Constant Pool Table)存储了类在编译期间生成的字面量、符号引用,这些信息在类加载完后会被解析到运行时常量池中
1269512708

1269612709
**运行时常量池**是方法区的一部分
1269712710

@@ -12708,12 +12721,9 @@ public static void main(String[] args) throws InterruptedException {
1270812721

1270912722
##### 本地内存
1271012723

12711-
本地内存:又叫做**堆外内存**,线程共享的区域,本地内存这块区域是不会受到JVM的控制的,也就是说对于这块区域是不会发生GC的,因此对于整个java的执行效率是提升非常大
12724+
虚拟机内存:Java虚拟机在执行的时候会把管理的内存分配成不同的区域,受虚拟机内存大小的参数控制,当大小超过参数设置的大小时就会报OOM
1271212725

12713-
常量池:
12714-
12715-
* 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
12716-
* 运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
12726+
本地内存:又叫做**堆外内存**,线程共享的区域,本地内存这块区域是不会受到JVM的控制的,不会发生GC;因此对于整个java的执行效率是提升非常大,但是如果内存的占用超出物理内存的大小,同样也会报OOM
1271712727

1271812728
本地内存概述图:
1271912729

@@ -12727,7 +12737,7 @@ public static void main(String[] args) throws InterruptedException {
1272712737

1272812738
##### 元空间
1272912739

12730-
PermGen 被元空间代替,其他内容比如**类信息、字段、静态属性、方法、常量池**等都移动到元空间区,元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现
12740+
PermGen 被元空间代替,永久代的**类信息、方法、常量池**等都移动到元空间区
1273112741

1273212742
元空间与永久代区别:元空间不在虚拟机中,使用的本地内存,默认情况下,元空间的大小仅受本地内存限制
1273312743

@@ -12784,9 +12794,10 @@ public class Demo1_8 extends ClassLoader { // 可以用来加载类的二进制
1278412794

1278512795
Direct Memory特点:
1278612796

12787-
* 常见于 NIO 操作时,用于数据缓冲区
12797+
* 常见于 NIO 操作时,用于数据缓冲区,使用native函数直接分配堆外内存
1278812798
* 分配回收成本较高,但读写性能高
1278912799
* 不受 JVM 内存回收管理
12800+
* 大大提高IO性能,避免了在java堆和native堆来回复制数据
1279012801

1279112802
分配和回收原理:
1279212803

@@ -12828,6 +12839,54 @@ public class Demo1_27 {
1282812839

1282912840

1283012841

12842+
12843+
12844+
***
12845+
12846+
12847+
12848+
#### 变量位置
12849+
12850+
类变量:
12851+
12852+
* 类变量是用static修饰符修饰,定义在方法外的变量,随着java进程产生和销毁
12853+
* 在java8之前把静态变量存放于方法区,在java8时存放在堆中
12854+
12855+
12856+
成员变量:
12857+
12858+
* 成员变量是定义在类中,但是没有static修饰符修饰的变量,随着类的实例产生和销毁,是类实例的一部分
12859+
* 在类初始化的时候,从运行时常量池取出直接引用或者值,与初始化的对象一起放入堆中
12860+
12861+
局部变量:
12862+
12863+
* 局部变量是定义在类的方法中的变量
12864+
* 在所在方法被调用时放入虚拟机栈的栈帧中,方法执行结束后从虚拟机栈中弹出,
12865+
12866+
常量:`final`关键字并不影响在内存中的位置
12867+
12868+
类常量池、运行时常量池、字符串常量池有什么关系?有什么区别?
12869+
12870+
* 类常量池与运行时常量池都存储在方法区,而字符串常量池在jdk7时就已经从方法区迁移到了java堆中
12871+
* 在类编译过程中,会把类元信息放到方法区,类元信息的其中一部分便是类常量池,主要存放字面量和符号引用,而字面量的一部分便是文本字符
12872+
* 在类加载时将字面量和符号引用解析为直接引用存储在运行时常量池
12873+
* 对于文本字符来说,会在解析时查找字符串常量池,查出这个文本字符对应的字符串对象的直接引用,将直接引用存储在运行时常量池
12874+
12875+
什么是字面量?什么是符号引用?
12876+
12877+
* 字面量:java代码在编译过程中是无法构建引用的,字面量就是在编译时对于数据的一种表示
12878+
12879+
```java
12880+
int a=1;//这个1便是字面量
12881+
String b="iloveu";//iloveu便是字面量
12882+
```
12883+
12884+
* 符号引用:在编译过程中并不知道每个类的地址,因为可能这个类还没有加载,如果在一个类中引用了另一个类,无法知道它的内存地址,只能用他的类名作为符号引用,在类加载完后用这个符号引用去获取内存地址
12885+
12886+
例如:在com.demo.Solution类中引用了com.test.Quest,把`com.test.Quest`作为符号引用存进类常量池,在类加载完后,用这个符号引用去方法区找这个类的内存地址
12887+
12888+
12889+
1283112890
***
1283212891

1283312892

@@ -14977,6 +15036,17 @@ public class Candy11 {
1497715036

1497815037

1497915038

15039+
类加载方式:
15040+
15041+
* 隐式加载:
15042+
* 创建类对象、使用类的静态域、创建子类对象、使用子类的静态域
15043+
* 在JVM启动时,通过三大类加载器加载class
15044+
* 显式加载:
15045+
* ClassLoader.loadClass(className),只加载和连接,不会进行初始化
15046+
* Class.forName(String name, boolean initialize,ClassLoader loader),使用loader进行加载和连接,根据参数initialize决定是否初始化
15047+
15048+
15049+
1498015050
***
1498115051

1498215052

@@ -14995,25 +15065,18 @@ public class Candy11 {
1499515065

1499615066
- 从 ZIP 包读取,成为 JAR、EAR、WAR 格式的基础
1499715067
- 从网络中获取,最典型的应用是 Applet
14998-
- 运行时计算生成,例如动态代理技术,在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass 的代理类的二进制字节流
15068+
- 运行时计算生成,例如动态代理技术,在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass
1499915069
- 由其他文件生成,例如由 JSP 文件生成对应的 Class 类
1500015070

15001-
将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field
15071+
将类的字节码载入方法区中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field:
1500215072

1500315073
* _java_mirror 即 java 的类镜像,例如对 String 来说,就是 String.class,作用是把 class 暴露给 java 使用
15004-
* _super 即父类
15005-
* _fields 即成员变量
15006-
* _methods 即方法
15007-
* _constants 即常量池
15008-
* _class_loader 即类加载器
15009-
* _vtable 虚方法表
15010-
* _itable 接口方法表
15011-
* 如果这个类还有父类没有加载,先加载父类
15012-
* 加载和链接可能是交替运行的
15074+
* _super 即父类、_fields 即成员变量、_methods 即方法、_constants 即常量池、_class_loader 即类加载器、_vtable 虚方法表、_itable 接口方法表
1501315075

1501415076
注意:
1501515077

15016-
* instanceKlass 元数据是存储在方法区(1.8 后的元空间内),但 _java_mirror是存储在堆中
15078+
* 如果这个类还有父类没有加载,先加载父类
15079+
* 加载和链接可能是交替运行的
1501715080
* instanceKlass和_java_mirror相互持有对方的地址,堆中对象通过instanceKlass和元空间进行交互
1501815081

1501915082
<img src="https://gitee.com/seazean/images/raw/master/JavaSE/JVM-类的生命周期-加载.png" style="zoom:80%;" />
@@ -15034,7 +15097,7 @@ public class Candy11 {
1503415097

1503515098
##### 准备
1503615099

15037-
类变量是被 static 修饰的变量,准备阶段为**静态变量分配内存并设置初始值**,使用的是方法区的内存
15100+
类变量是被 static 修饰的变量,准备阶段为**静态变量分配内存并设置初始值**,使用的是方法区的内存
1503815101

1503915102
* 类变量也叫静态变量,就是在变量前加了static 的变量
1504015103
* 实例变量也叫对象变量,即没加static 的变量
@@ -15068,7 +15131,7 @@ public class Candy11 {
1506815131

1506915132
将常量池的符号引用替换为直接引用(内存地址)的过程
1507015133

15071-
* 符号引用:一组符号来描述目标,可以是任何字面量属于编译原理方面的概念如:包括类和接口的全限名、字段的名称和描述符、方法的名称和描述符
15134+
* 符号引用:一组符号来描述目标,可以是任何字面量属于编译原理方面的概念如:包括类和接口的全限名、字段的名称和描述符、方法的名称和描述符
1507215135
* 直接引用:直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
1507315136

1507415137
其中解析过程在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 的动态绑定。
@@ -15125,6 +15188,8 @@ class D {
1512515188
* 虚拟机会保证一个类的 <clinit>() 方法在多线程环境下被正确的加锁和同步,如果多个线程同时始化一个类,只会有一个线程执行这个类的 <clinit>() 方法,其它线程都阻塞等待,直到活动线程执行 <clinit>() 方法完毕
1512615189
* 如果在一个类的 <clinit>() 方法中有耗时的操作,就可能造成多个线程阻塞,在实际过程中此种阻塞很隐蔽
1512715190

15191+
JVM 在对static变量内存分配的时候,对类的处理顺序是:类{}块—>类构造函数—>static 块—>再是方法块
15192+
1512815193
特别注意的是,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问
1512915194

1513015195
```java
@@ -15147,28 +15212,30 @@ public class Test {
1514715212

1514815213
init指的是实例构造器,主要作用是在类实例化过程中执行,执行内容包括成员变量初始化和代码块的执行
1514915214

15150-
初始化即调用 <cinit>()V ,虚拟机会保证这个类的构造方法的线程安全,先为实例变量分配内存空间,再执行赋默认值,然后根据源码中的顺序执行赋初值或代码块,没有成员变量初始化和代码块则不会执行
15215+
初始化即调用 <init>()V ,虚拟机会保证这个类的构造方法的线程安全,先为实例变量分配内存空间,再执行赋默认值,然后根据源码中的顺序执行赋初值或代码块,没有成员变量初始化和代码块则不会执行
1515115216

1515215217
创建对象的过程:**父类的类构造器<clinit>() -> 子类的类构造器<clinit>() -> 父类的成员变量和实例代码块 -> 父类的构造函数 -> 子类的成员变量和实例代码块 -> 子类的构造函数**
1515315218

1515415219

1515515220

15221+
15222+
1515615223
###### 初始化时机
1515715224

1515815225
类的初始化是懒惰的
1515915226

1516015227
**主动引用**:虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化(加载、验证、准备都会随之发生):
1516115228

1516215229
* 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化,最常见的生成这 4 条指令的场景是:
15163-
* 使用 new 关键字实例化对象时
15164-
* 读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外,即变量)
15165-
* 调用一个类的静态方法时
15230+
* new:使用 new 关键字实例化对象时
15231+
* getstatic:程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)
15232+
* putstatic:程序给类的静态变量赋值
15233+
* invokestatic :调用一个类的静态方法时
1516615234
* 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化
15167-
1516815235
* 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
15169-
1517015236
* 当虚拟机启动时,需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类
15171-
* 当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化
15237+
* MethodHandle和VarHandle可以看作是轻量级的反射调用机制,而要想使用这2个调用, 就必须先使用findStaticVarHandle来初始化要调用的类
15238+
* 补充:当一个接口中定义了JDK8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化
1517215239

1517315240
**被动引用**:所有引用类的方式都不会触发初始化,称为被动引用
1517415241

@@ -15187,6 +15254,16 @@ init指的是实例构造器,主要作用是在类实例化过程中执行,
1518715254

1518815255
执行了System.exit()方法,程序正常执行结束,程序在执行过程中遇到了异常或错误而异常终止,由于操作系统出现错误而导致Java虚拟机进程终止
1518915256

15257+
卸载类即该类的Class对象被GC
15258+
15259+
卸载类需要满足3个要求:
15260+
15261+
1. 该类的所有的实例对象都已被GC,也就是说堆不存在该类的实例对象
15262+
2. 该类没有在其他任何地方被引用
15263+
3. 该类的类加载器的实例已被GC
15264+
15265+
在JVM生命周期类,由jvm自带的类加载器加载的类是不会被卸载的,但是由我们自定义的类加载器加载的类是可能被卸载
15266+
1519015267

1519115268

1519215269

0 commit comments

Comments
 (0)