定义:java virtual meachine -java运行时环境(java二进制字节码的运行环境)。
好处:
java程序 -->(编译)--> 字节码文件 --> 解释器 --> 机器码 --> 操作系统(Win,Linux,Mac JVM)
由于跨平台的设计,java的指令都是根据栈来设计的,不同平台CPU架构不同,所以不能设计为基于寄存器的。
1.启动
通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现指定的.
2.执行
3.退出
作用
是当前线程锁执行的字节码的行号指示器,字节码解释器通过改变计数器的值来选取下一条需要执行的字节码指令。
特点
作用
是线程运行时需要的内存空间。
特点
注意
栈内存溢出的原因
线程诊断
cpu占用过多
作用
与虚拟机栈类似,只不过本地方法栈是为虚拟机使用到的Native方法服务。
是虚拟机中所管理的内存中最大的一块,通过new关键字创建的对象,都存放在堆内存中,被所有的线程所共享,在虚拟机启动时被创建,具有垃圾回收机制。
堆内存溢出
一个简单的例子 java.lang.OutOfMemeryError:Java Heap space
/**
* 演示堆内存溢出 java.lang.OutOfMemoryError: Java heap space
* -Xmx8m
*/
public class Demo1_5 {
public static void main(String[] args) {
int i = 0;
try {
List list = new ArrayList<>();
String a = "hello";
while (true) {
list.add(a); // hello, hellohello, hellohellohellohello ...
a = a + a; // hellohellohellohello
i++;
}
} catch (Throwable e) {
e.printStackTrace();
System.out.println(i);
}
}
}
程序执行过程中,list对象始终被关联,无法被回收,死循环不断将list规模变大,最终大于堆内存大小,内存溢出。
堆内存诊断
和堆一样,是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息(方法、构造器)、常量、静态变量等,在虚拟机启动时被创建。
垃圾回收在方法区较少出现,主要针对常量池的回收和对类型的卸载。
组成与实现: jdk6(永久代实现)和jdk8(元空间实现)中方法区的区别,其中最主要的区别是jdk8中将方法区转移到本地内存中,且常量池分为运行时常量池和字符串常量池;且字符串常量池被留在内存中的堆中。
方法区的结构:
常量池,运行时常量池和字符串池
全局字符串池(string pool也有叫做string literal pool):
class文件常量池(class constant pool):class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译 器生成的各种字面量(Literal)和符号引用(Symbolic References)。
运行时常量池:
举例说明
public class HelloWorld {
public static void main(String []args) {
String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);//true
System.out.println(str2 == str4);//false
System.out.println(str4 == str5);//true
}
}
1. 编译期优化
String s1 = "a";
“a”的时候把对象放入串池
String s2 = "b";
String s3 = "ab";
/*
new StringBuilder().append("a").append("b").toString();
s1 s2 为变量,其值没有确定,s4为新new出的对象,存放在堆中
*/
String s4 = s1+s2;
System.out.println(s4==s3);//false
/*
javac编译期优化,“a”,“b”已经确定,所以在编译期s5就已经确定为“ab”
在串池中寻找到“ab”,返回“ab”引用值
*/
String s5 = "a"+"b";
System.out.println(s3==s5);//true
2. 字符串延迟实例化
String s1 = "a";
//java.lang.string.count = 2361
String s2 = "b";
//java.lang.string.count = 2362
String s3 = "ab";
//java.lang.string.count = 2363
3. 常量池和串池的关系
4. intern方法的运用jdk1.8
public class internDemo {
public static void main(String[] args) {
String s = new String("a") + new String("b");
//"a"把对象放入串池,不是new String("a")放入,new放入的是堆
//new String("a") new String("b") new String("ab")
//StringTable["a","b"] "ab"属于动态拼接,不会加入StringTable,s在堆中
//使用intern方法 在StringTable中放入此字符串对象,如果有则返回给字符串对象,没有在放入
String s2 = s.intern(); //s放入串池
//s2指向的是串池中字符串对象 s也被放入串池中
System.out.println(s==s2);//true
}
}
public class internDemo {
public static void main(String[] args) {
String s3 = "ab";
//new String("a") new String("b") new String("ab")
//StringTable["a","b","ab"]
String s = new String("a") + new String("b");
//使用intern方法 在StringTable中放入此字符串对象,如果有则返回给字符串对象,没有再放入,此时串池中实际上已经有了“ab”,因此返回了串池中的“ab”
String s2 = s.intern();
//s2指向的是串池中字符串对象 s未被放入串池,还在堆中
System.out.println(s3==s2);//true
System.out.println(s3==s);//false
}
}
5. intern方法的运用jdk1.6
public class internDemo {
public static void main(String[] args) {
//new String("a") new String("b") new String("ab")
//StringTable["a","b"]
String s = new String("a") + new String("b");
//使用intern方法 在StringTable中放入此字符串对象,如果有则返回给字符串对象,没有则复制一份后放入,因此串池中的“ab”和s是不同的对象
String s2 = s.intern();
//s2指向的是串池中字符串对象(s复制后的对象) ,s未被放入串池,还在堆中
//StringTable["a","b","ab"]
String s3 = "ab";
System.out.println(s3==s2);//true
System.out.println(s3==s);//flase,s还在堆中,s3在常量池中
}
}
6. StringTable位置
7. StringTable调优
不是虚拟机运行时内存的一部分,而是系统内存。
直接内存使用前和使用后
不使用直接内存:
使用直接内存:
直接内存不受JVM内存回收管理,那么如何进行垃圾回收?看一下内存溢出的情况:
package com.example.jvm;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class Demo3 {
public static void main(String[] args) {
List list = new ArrayList<>();
int i = 0;
try {
while(true){
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100*1024*1024);
list.add(byteBuffer);
i++;
}
}finally {
System.out.println(i);
}
}
}
36 3.6G
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:694)
at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at com.example.jvm.Demo3.main(Demo3.java:14)
直接内存的分配和回收原理