JVM简介

JVM包含:java栈、堆、本地方法栈、程序计数器、方法区

堆:它是jvm所管理的区域中占有内存最大的一块区域,大部分的GC都发生在堆上,基本上所有的对象的实例都在其中分配内存(并不是所有https://blog.csdn.net/w372426096/article/details/80333657,如果不发生逃逸的话,可以开启逃逸分析配置,将不逃逸的对象内存分配由堆上变为栈上)
java栈:java方法执行时会将一个栈帧压入栈中,方法的执行过程就是栈帧的入栈出栈过程,栈帧中存储了方法中使用的局部变量(基本数据类型、对象的引用等)、中间计算结果、方法返回出口信息等
本地方法栈:与Java栈类似,不同的是Java栈为java方法服务,而本地方法栈为native方法服务
程序计数器:java文件是被编译成字节码执行的,它用于指明当前执行到了哪一行
方法区:方法区用于存储已经被加载的类信息、方法信息、静态变量、常量等数据,常量池也属于方法区
其中堆、方法区为线程共享,当虚拟机启动时创建,而java栈、本地方法栈、程序计数器为线程私有,它的生命周期与线程的相同

JVM中对象的访问定位方式:https://blog.csdn.net/liupeifeng3514/article/details/79111651
在java栈中有reference数据,它是对象的引用,jvm利用它来定位堆中的对象,包含以下两种方式:
(1)reference指向的是java堆中的句柄池中的某一句柄,该句柄指向了堆中对象内存的位置和方法区中对象的类型信息
(2)使用直接指针,reference指向的直接是堆中对象的内存地址,对象内存地址中存储了该对象的类型信息在方法区中的位置
这两种方式各有特点,使用句柄的好处是当对象的位置发生改变时(垃圾回收时对象位置会发生变动),是句柄指向的地址信息会发生改变,而reference指向的地址不会改变;而直接指向对象地址的好处就是速度更快,因为减少了一次位置定位的过程,JVM中就是使用了这种方式

JVM中栈、堆、方法区调用过程:https://blog.csdn.net/heart_mine/article/details/79497209
AppMain.java
public class AppMain {
  public static void main() {
    Test test1 = new Test();
    test1.testMethod();
  }
}
执行命令java AppMain,编译Java文件,生成AppMain和Test类字节码文件,启动一个jvm进程,加载AppMain.class字节码文件,将类信息存储进方法区中,然后定位到方法区中main方法,开始执行,首先在java栈中找test1引用,发现并没有,这个时候Test类的对象还没有被创建。然后jvm会加载Test()类,将Test类信息、方法信息存储进方法区中,接着创建Test对象,在堆中分配内存,接着创建一个Test对象的引用test1,将它放在main方法线程栈中,创建工作就完成了。执行test1.testMethod();语句时,会先在栈中找打test1,根据该引用定位Test对象在堆中的位置,堆中会存储该对象类信息和方法信息在方法区中的位置,找到testMethod()方法的信息,并执行该方法。

JVM垃圾回收:who(谁需要被回收) how(怎么样回收) when(什么时候回收) https://www.cnblogs.com/1024Community/p/honery.html#22-%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90%E7%AE%97%E6%B3%95
who
哪些内存需要回收:在jvm分区中java栈、本地方法栈、程序计数器是线程私有的,随线程创建而创建,随线程死亡而回收不用太多的考虑回收的问题,GC主要发生在堆和方法区中。
方法区如何判断是否需要回收?
方法区中回收主要包含无用常量、无用的类信息,如果常量没有再被引用时就可以被回收,类信息则是对应类的实例和类加载器已经被全部回收。
如何判断堆中一个对象是否可以被回收?
(1)引用记数法:每个对象都有一个变量,当对象被创建时后,将其地址分配给某一引用时,该变量初始为1,每当有其他引用也指向该对象内存地址,则变量加1,反之减1,当该变量为0时,说明没有引用再指向它了,可以被回收。优点:简单易操作,缺点:会有对象间循环引用的缺陷
(2)可达性分析:从一个根节点GC ROOT点开始寻找它所引用的对象,找到这个对象后继续以该对象为新的节点寻找它所引用的对象,直到找到所有叶子节点。而其他没有被包含在其中的节点则为可回收节点。
GC ROOT节点的选取可以从栈中引用、方法区中静态属性引用的对象、方法区中常量引用的对象中选取,通过一系列GC ROOT来搜索可回收对象。

how
常见的GC算法有哪些?
标记-清除算法
在可达性分析中,通过对一系列GC ROOT搜寻其引用,对这些存活的对象都添加一个存活标记,当搜索完成后,对那些没有标记的对象,统一的从内存中进行回收清理,由于该种方式是直接回收对象内存,没有进行移动因此会造成内存碎片
标记-整理算法
与标记清除法类似,只是在进行未标记对象内存回收后,会将存活的对象向左端移动,并更新它们的引用地址,解决了内存碎片问题。
分代回收算法
现在大部分虚拟机使用该方式,将堆区分为新生代、老年代,在堆外还有一个持久代,在新生代中每次回收都会有大量的对象被回收,老年代中对象被回收较少,持久代中基本不被回收。
新生代中按照8:1:1分为1个eden区2个survivor区,假设一个是s1,一个为s2,s1,s2时刻保持一个为空,对象在堆中初次被分配时,存放在eden区中,当发现eden区满了之后会进行一次垃圾回收,将剩余存活的对象放在s1中,此时s2是空的,如果某次eden区回收将剩余存活对象放入s1时后发现s1放不下,s1也会进行GC,将eden区和s1区剩余存活对象放入s2中,此时s1为空,下次eden区再回收时就是向s2中存放存活对象,但如果eden区和s1的存活对象过多,s2放不下,则会将这些存活对象放入老年代中。同时如果一个对象经过多次垃圾回收依然存活,它也会被放入老年代。年轻代发生的GC叫做minor GC
老年代:当老年代满了以后会发生full GC,此时年轻代、老年代、持久代都发生GC
持久代:持久代一般指的是方法区,jdk 1.8后,方法区被元空间所代替

when
什么时候会发生垃圾回收?
GC根据分类可以分为年轻代的GC和full GC
年轻代GC:当eden区满了以后就会发生
full GC:老年代满了,持久代满了、主动调用system.gc()

你可能感兴趣的:(JVM)