一、JVM体系结构
Java栈、本地方法栈、程序计数器:线程私有(每个线程拥有一份)、内存很小,几乎不存在垃圾回收
方法区、堆:线程共享(一个进程中只有一份)
二、类加载器(ClassLoader)
class文件开头有特定的标识——cafe.babe
1)类加载器分为四类(面试答前三个就好):
1、启动类加载器:启动就加载rt.jar
2、扩展类加载器:加载jre\lib\ext文件夹下的jar包,包的名字为javax.xxx.xxx
3、应用程序类记载器
4、自定义类加载器
代码案例
public class MyObject {
public static void main(String[] args) {
Object object = new Object();
System.out.println(object.getClass().getClassLoader().getParent().getParent());//Exception in thread "main" java.lang.NullPointerException
System.out.println(object.getClass().getClassLoader().getParent());//Exception in thread "main" java.lang.NullPointerException
System.out.println(object.getClass().getClassLoader());//null
System.out.println("========================");
MyObject myObject = new MyObject();
System.out.println(myObject.getClass().getClassLoader().getParent().getParent());//null
System.out.println(myObject.getClass().getClassLoader().getParent());//sun.misc.Launcher$ExtClassLoader@4554617c
System.out.println(myObject.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2
}
}
2)双亲委派机制:要加载类,先根据包名类名逐级往上请求,BootStrap->Extension->AppClassLoader
3)沙箱安全机制:防止自定义代码污染到Java源代码
-
运行报错:在类java.lang.String中找不到main方法
三、本地方法栈、本地方法接口(native)
线程、进程不是语言级的,是操作系统级别的。
本地方法栈是为虚拟机使用到native关键字定义的方法服务的,如果是普通方法就放Java栈,如果是native定义的方法就放本地方法栈,native方法实现交给底层的函数库
Thread t1 = new Thread();
t1.start();
t1.start();
运行报错:Exception in thread "main" java.lang.IllegalThreadStateException,不合法的多线程运行状态
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
四、程序(PC)寄存器
五、方法区
方法区供线程共享,它存储每一个类的结构信息,例如:常量池、字段(静态变量)、方法数据、构造函数和普通方法的字节码内容
PS:实例变量存储在堆内存中,和方法区无关。方法区是一个规范,类似于接口,其落地实现是Java8的元空间或Java7之前的持久代。
六、Java栈(重点)
栈管运行,堆管存储
1、栈保存8种基本类型的变量+对象的引用变量+实例方法
2、Java栈中会创建栈帧来存储方法
栈帧中主要保存3类数据:
1)本地变量:输入参数(形参)、输出参数(return数据)、方法内的变量
2)栈操作:记录出栈和入栈的操作
3)栈帧数据:包括类文件(定义在方法内的局部内部类)、方法等
这不是异常,这是错误。。。
HotSpot=JDK cmd打java -version
元数据=Class=类的结构信息
七、堆(重点)
1、堆从物理上分为新生代和老年代
逻辑上分为新生代、老年代、元空间(Java8)
Java8以后的元空间并不在虚拟机中而是使用本机物理内存
堆内存的初始值默认是物理内存的1/64、最大值是是物理内存的1/4
public class Heap {
public static void main(String[] args) {
//查看CPU的核数
System.out.println(Runtime.getRuntime().availableProcessors());
//最大可以使用内存
long maxMemory = Runtime.getRuntime().maxMemory();
//初始默认内存
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("-Xmx:MAX_MEMORY=" + maxMemory + "(字节)" + (maxMemory / 1024 / 1024) + "MB");
System.out.println("-Xms:TOTAL_MEMORY=" + totalMemory + "(字节)" + (totalMemory / 1024 / 1024) + "MB");
}
}
运行结果:
配置堆内存:一般将默认内存大小跟最大可使用内存配置一样大
-Xms——start
-Xmx——max
运行结果:
演示堆内存溢出(OOM):先将堆内存改为10MB方便演示,然后运行下面代码
public class Heap {
public static void main(String[] args) {
String str = "hello world";
while (true) {
str += str + new Random().nextInt(888888)+ new Random().nextInt(999999999);
}
//byte[] bytes = new byte[40*1024*1024];//40MB
}
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:647)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at cn.wyu.jvm.Heap.main(Heap.java:14)
GC是什么(分代收集算法):没有最好的GC算法,分代,分情况考虑
1)次数上频繁收集Young区
2)次数上较少收集Old区
3)基本不动元空间
Java在进行GC的时候并非每次都对上面三个内存区域一起回收的,大部分时候都回收的是新生代
因此GC又分为两种类型:一种是普通的GC(minor GC)负责回收新生代,一种是全局GC(Full GC)负责回收老年代
minor GC和Full GC的区别:
minor GC回收的速度一般比Full GC快10倍以上,因为新生代空间与老年代空间的大小比是1:2,也就是说新生代空间小。
GC四大算法
1)引用计数法
2)复制算法
新生代特点:区域相对较小,对象存活率低
新生代使用的minor GC,这种GC算法采用的是复制算法,不会产生内存碎片,但是耗内存,如果to区满了话,移动到老年区
3)标记清除
老年代特点:区域较大,对象存活率高
老年代一般是使用标记清除或者是标记清除与标记压缩整合的算法
原理:
优点:节省空间
缺点:效率比复制算法慢,会产生内存碎片
4)标记压缩
优点:没有内存碎片
缺点:耗时最长
基本类型传的是复印件,引用类型传的是内存地址
案例笔试题:
public class TestTransferValue {
public void changeValue1(int age){
age = 30;
}
public void changeValue2(Person person){
person.setPersonName("xxx");
}
public void changeValue3(String str){
str="xxx";
}
public static void main(String[] args) {
TestTransferValue test = new TestTransferValue();
int age = 20;
test.changeValue1(age);
System.out.println("age===="+age);
Person person = new Person();
test.changeValue2(person);
System.out.println("personName===="+person.getPersonName());
String str = "abc";
test.changeValue3(str);
System.out.println("String===="+str);
}
}
class Person{
private String personName;
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
}
运行结果:
图形理解 :
八、JMM
1、volatile是Java虚拟机提供的轻量级的同步机制,只保证可见性、不保证原子性、禁止指令重排
2、JMM的特性:
1)可见性
2)原子性
3)有序性
可见性代码演示:
class MyNumber{
volatile int number = 10;
public void addTo1024(){
this.number = 1024;
}
}
public class Heap {
public static void main(String[] args) {
MyNumber myNumber = new MyNumber();
new Thread(() ->{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myNumber.addTo1024();
System.out.println(Thread.currentThread().getName()+"\t"+myNumber.number);
},"AA").start();
while (myNumber.number == 10){
//需要有通知机制,告诉main线程,number变成1024了,跳出循环
}
System.out.println(Thread.currentThread().getName()+"over");
}
}
运行结果:
如果不加volition,main线程将在while里面一直循环,跳不出来
运行结果: