堆
堆(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.classLast modified 2024年2月6日; size 780 bytesSHA-256 checksum 56c70e4c76c31646a9cb2cfece3728bc1a10f96185b4456e087039d1e60a0541Compiled from "Temp.java"
public class bean.Tempminor version: 0major version: 65flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #17 // bean/Tempsuper_class: #2 // java/lang/Objectinterfaces: 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: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 3: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=5, args_size=10: ldc #7 // String a2: astore_13: ldc #9 // String b5: astore_26: ldc #11 // String ab8: astore_39: aload_110: aload_211: invokedynamic #13, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;16: astore 418: returnLineNumberTable:line 5: 0line 6: 3line 7: 6line 8: 9line 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方法来释放直接内存