堆
堆(Heap)
- 通过new关键字,创建的对象都会使用堆内存
- 特点:
- 他是线程共享的,堆中的对象需要考虑线程安全的问题
- 有垃圾回收机制
堆内存溢出(OutOfMemoryError)
代码演示
List<String> list = new ArrayList<>();
try{
String a = "hello";
while(true){
list.add(a);
a = a + a;
}
}catch(Throwable e){
e.printStackTrace()
}
堆内存诊断
- jps工具:查看当前系统中有哪些java进程
- jmap工具:查看堆内存占用情况(jmap -heap 进程id)
- jconsole工具:图形界面的,多功能的检测工具,可以连续检测
方法区
内存溢出
- 1.8以前会导致永久代内存溢出
- 1.8以后会导致原空间内存溢出
运行时常量池
二进制字节码包含:类基本信息,常量池,类方法定义包含了虚拟机指令
- 常量池就是一张表,虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量等信息
- 运行时常量,常量池是*.class文件中的,当该类被加载时,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
StringTable
底层是基于哈希表实现,不能扩容
package bean;
public class Temp {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;
System.out.println(s3==s4);//false
/*
解释:
1. s3是在常量池中
2. s4是new出来的字符串,在堆中
3. 二者内存空间地址不一样,所以是false
*/
}
}
执行javap -v Temp.class
Classfile /E:/code/java/temp/Thread/reflect/src/main/java/bean/Temp.class
Last modified 2024年2月6日; size 780 bytes
SHA-256 checksum 56c70e4c76c31646a9cb2cfece3728bc1a10f96185b4456e087039d1e60a0541
Compiled from "Temp.java"
public class bean.Temp
minor version: 0
major version: 65
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #17 // bean/Temp
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool://这里是常量池
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = String #8 // a
#8 = Utf8 a
#9 = String #10 // b
#10 = Utf8 b
#11 = String #12 // ab
#12 = Utf8 ab
#13 = InvokeDynamic #0:#14 // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#14 = NameAndType #15:#16 // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#15 = Utf8 makeConcatWithConstants
#16 = Utf8 (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#17 = Class #18 // bean/Temp
#18 = Utf8 bean/Temp
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 SourceFile
#24 = Utf8 Temp.java
#25 = Utf8 BootstrapMethods
#26 = String #27 // \u0001\u0001
#27 = Utf8 \u0001\u0001
#28 = MethodHandle 6:#29 // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#29 = Methodref #30.#31 // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#30 = Class #32 // java/lang/invoke/StringConcatFactory
#31 = NameAndType #15:#33 // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#32 = Utf8 java/lang/invoke/StringConcatFactory
#33 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#34 = Utf8 InnerClasses
#35 = Class #36 // java/lang/invoke/MethodHandles$Lookup
#36 = Utf8 java/lang/invoke/MethodHandles$Lookup
#37 = Class #38 // java/lang/invoke/MethodHandles
#38 = Utf8 java/lang/invoke/MethodHandles
#39 = Utf8 Lookup
{
public bean.Temp();
descriptor: ()V
flags: (0x0001) 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 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: ldc #7 // String a
2: astore_1
3: ldc #9 // String b
5: astore_2
6: ldc #11 // String ab
8: astore_3
9: aload_1
10: aload_2
11: invokedynamic #13, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
16: astore 4
18: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
line 8: 9
line 9: 18
}
SourceFile: "Temp.java"
BootstrapMethods:
0: #28 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#26 \u0001\u0001
InnerClasses:
public static final #39= #35 of #37; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
- StringTable特性
- 常量池中的字符串仅是符号,第一次用到时才变为对象
- 利用串池的机制,来避免重复创建字符串对象
- 字符串拼接原理是StringBuilder(1.8)
- 字符串常量拼接的原理是编译期优化
- 可以使用intern方法,可以主动将串池中还没有的字符串对象放入串池(1.8尝试将字符串对象放入串池,如果有则并不会放入,如果没有,则会把串池中的对象返回)
- StringTable位置
- 1.6之前,StringTable在永久代内存空间中
- 1.8以后,StringTable在堆内存空间中
- StringTable垃圾回收
- 超过阈值,会触发GC垃圾回收
- StringTable性能调优
- StringTable底层是基于哈希表实现的,所以StringTable调优,就是优化底层哈希表的大小,以此减少哈希碰撞
- 参数
-XX:StringTableSize = 桶个数
- 考虑将字符串对象是否入池(包含很多重复的字符串)
直接内存
定义:(操作系统内存)
1. 常见与NIO操作,用于数据缓冲区
2. 分配回收成本较高,但读写性能高
3. 不受JVM内存回收管理
内存分配和释放原理
是通过一个对象unsafe
来分配和释放内存的,并且回收需要主动调用freeMemory方法
ByteBuffer的实现类内部,使用了Cleaner(虚引用)
来检测ByteBuffer对象,一旦该对象被GC垃圾回收机制回收,那么就会由ReferenceHandler
线程通过Cleaner
的clean
方法调用freeMemory方法来释放直接内存