1.JVM模型
按照线程来讲的话住要分为两种,一种是线程独占的,一个是线程共享的。
线程共享的有方法区,堆。
线程独占的有程序计数器,本地方法栈和虚拟机栈。
方法区的话是一个java虚拟机的模型规范。然后具体实现的话是元空间和永久代,永久代在1.8之后被移除了,元空间的话他是发布在计算机内存的,脱离了JAVA虚拟机内存。是独立存在的。
- 程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程私有。
- Java虚拟栈:存放基本数据类型、对象的引用、方法出口等,线程私有。
- Native方法栈:和虚拟栈相似,只不过它服务于Native方法,线程私有。
- Java堆:java内存最大的一块,所有对象实例、数组都存放在java堆,GC回收的地方,线程共享。
- 方法区:它存储已被Java虚拟机加载的类信息,即时编译器编译后的代码等。
2.栈堆同时建立
AA aa=new AA();//这里AA是一个自定义类名
这里在栈上面建立了aa的位置,在堆中建立了AA类的对象。
这里堆内存的对象被指向到栈内存地址aa。
static String aa=”AA”(或String aa=new String(“AA”));
其中aa就是就是保存在栈中的,被创建出来的”AA”就是保存在堆中的,然后被指向到某一个栈地址,如果没有静态static,这里的aa也是保存在堆中的。
3.GC算法
当某个对象使用完毕后就会出栈,但是所指向的对象就会留在堆中。就变成了堆里面的垃圾。而堆内的垃圾又分为三种区域,新生区,老年区和元空间。而GC算法就相当是一个垃圾回收机制,回收掉新生区和老年区内部的垃圾。
垃圾回收算法(GC)
标记-清除算法:遍历内存区域,对需要回收的对象打上标记。再次遍历内存,对已经标记过的内存进行回收
缺点:容易产生大量内存碎片,当需要大量内存时,无法找到一块满足要求的。
复制算法:将内存分为相同的两等份。每次只是用其中的一份,当一份空间快用完时,就将存货的对象全部移动到另一部分,然后一次性清除到这块没有用的空间。
缺点:只能用一半内存。
标记整理:和标记清除法类似,只是在标记后让存活的对象向一边移动,然后直接清理掉没用的内存。
缺点:需要移动,消耗性能。
分代收集算法:针对新生代和老年代不同的区域,采用不同的算法。
新生代:每次都有大量对象死亡,所用使用复制算法。
老年代:对象存活时间长,采用标记整理,或者标记清除算法都可以。
4.垃圾回收器
CMS(并行垃圾回收器):是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片,仅作用于老年代的收集。
G1(Java8开始使用):重新定义了堆空间,打破了原有的分代模型,将堆划分为一个个区域。这么做的目的是在进行收集时不必在全堆范围内进行,这是它最显著的特点。区域划分的好处就是带来了停顿时间可预测的收集模型:用户可以指定收集操作在多长时间内完成,即 G1 提供了接近实时的收集特性。
G1 从整体来看是基于“标记-整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的。但无论如何,这两种算法都意味着 G1 运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。
ZGC(Java11,12新增)
5.如何判断是否是垃圾
引用计数法:给对象添加一个引用计数器,每当有一个地方引用的时候,计数器的值就 +1。当引用失效的时候,计数器的值就 -1。任何时刻计数器为0的对象就是不可能再被引用的
。
优点:简单、高效
问题:难以解决对象之间循环引用的问题。
可达性分析法:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链
,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
在Java语言中,可作为GC Roots的对象包括下面几种:
-
虚拟机栈(栈帧中的本地变量表)中引用的对象。
-
本地方法栈中JNI (即一般说的Native方法)引用的对象。
-
方法区中类静态属性引用的对象。
-
方法区中常量引用的对象。
6.类加载机制
1 加载
加载过程主要完成三件事情:
经过IO,经过classoader去查询那个文件。
通过类的全限定名来获取定义此类的二进制字节流
将这个类字节流代表的静态存储结构转为方法区的运行时数据结构
在堆中生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口。
这个过程主要就是类加载器完成。(对于HotSpot虚拟而言,Class对象较为特殊,其被放置在方法区而不是堆中)
2.验证
此阶段主要确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全。
3,准备
仅仅为类变量(static修饰的变量)分配内存空间并且设置该类变量的初始值(这里的初始值指的是数据类型默认的零值),这里不包含用final修饰的static,因为用final修饰的类变量在javac执行编译期间就会分配,同时要注意,这里不会为实例变量分配初始化。类变量会分配在方法区中,而实例变量会在对象实例化是随着对象一起被分配在java堆中
4解析:解析阶段主要是将常量池内的符号引用替换为直接引用的过程。符号引用是用一组符号来描述目标,可以是任何字面量,而直接引用则是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。通常而言一个符号引用在不同虚拟机实例翻译出来的直接引用一般不会相同。
5.初始化
类加载过程的最后一步,到该阶段才真正开始执行类中定义的java代码,同样该阶段也是初始化类变量和其他资源(执行static字段和静态代码块),换句话说该阶段是执行类构造器<clinit>()方法的过程。