自己学习java整理java虚拟机知识

一.什么是java虚拟机?
    java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。
    java虚拟机有自己想象的硬件,如处理器,堆栈,寄存器,还有响应的指令系统
二.为何使用java虚拟机?
    1.实现java的跨平台特性
    2.把目标代码编译成字节码
        应用程序层 (java应用程序) (java应用程序) (java应用程序)

        java平台层  (java虚拟机)    (java虚拟机)     (java虚拟机)
    
        操作系统层  (windows)       (UNIX Linux)    (RTOS)

        硬件层         (x86)               ( SPARC)         (MIPS/PPC)

三.java虚拟机的生命周期
    (1)JVM实例的诞生
        当启动一个Java程序时,一个JVM实例就产生了,
        任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点。

(2)JVM实例的运行
        main()作为该程序初始线程的起点,任何其他线程均由该线程启动。
        JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,
        守护线程通常由JVM自己使用,java程序也可以标明自己创建的线程是守护线程。

(3)JVM实例的消亡
        当程序中的所有非守护线程都终止时,JVM才退出;
        若安全管理器允许,程序也可以使用java.lang.Runtime类或者java.lang.System.exit()来退出。


四.java虚拟机的体系结构
     类装载器ClassLoader:用来装载.class文件
     执行引擎:执行字节码,或者执行本地方法
     运行时数据区:方法区、堆、Java栈、程序计数器、本地方法栈

五.java虚拟机中使用的数据类型
    1.原始数据类型
    2.引用数据类型
    3.在java虚拟机中还存在一个java语言中不能使用的原始数据类型
        返回值类型。这种类型被用来实现java程序中的“finally classes” 
    4.引用类型可能被创建为:类类型,接口类型,数组类型。他们都引用被动态创建的对象
    当引用类型引用null时,说明没有引用任何对象


六.java虚拟机内存区域

    方法区 }(线程共享区)   
    堆     
    
    虚拟机栈
    本地方法栈   }(线程私有)
    程序计数器


1.程序计数器:
    1)jvm将这个计数看作当前线程执行某条字节码的行数,会根据计数器的值
    来选取需要执行的操作语句。这个属于线程私有,不可共享,如果共享会导致计数
    混乱,无法准确的执行当前线程需要执行的语句
    2)该区域不会出现任何OutOfMemoryError的情况

2.虚拟机栈
    1)虚拟机栈就是指经常说到的栈内存。java中每一个方法从调用直至完成的过程
    就对应着一个栈帧在虚拟机中入栈到出栈的过程
    2)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError(栈溢出)异常
    如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError

3.本地方法栈
    1)本地方法栈用来执行本地方法,抛出异常的情况和虚拟机栈一样。而虚拟机栈用来执行java方法

4.堆
    1)是jvm中内存最大,线程共享的一块区域。唯一的目的是存储对象实例。这里
    也是垃圾收集器主要收集的区域。由于现代垃圾收集器采用的是分代收集算法,所有java堆
    也分为新生代和老年代。
    2)可以通过参数-Xmx(jvm最大可以内存)和-Xms(jvm初始内存)来调整内存,
    如果扩大至无法继续扩展时,会出现OutOfMemoryError的错误

5.方法区
    1)jvm中内存共享的一片区域,用来存储类信息,常量,静态变量,class文件
    垃圾收集器也会对这部分区域进行回收,比如常量持的清理和类型的卸载
    2)方法区内存不够用的时候,也会抛出OutOfMemoryError错误


七.虚拟机类加载机制的概念
    1)虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,
    转换解析和初始化,最终形成可以被虚拟机直接使用的java类型
    2)java语言里,类型的加载和连接过程是在程序运行期间完成的

八.类的生命周期
    加载 loading
    验证 verification
    准备 preparation
    解析 resolution
    初始化 initialization
    使用 using
    卸载 unloading

加载:
    1.通过一个类的全限定名来获取此类的二进制字节码
    2.将这个字节码所代表的静态存储结构转化为方法区的运行时数据结构
    3.在java堆中生成一个代表这个类的Class对象,作为方法区这些数据的访问入口
    
验证:
    1.虚拟机规范:验证输入的字节流是否符合Class文件的存储格式,否则抛出一个
    java.lang.VerifError异常
    2.文件格式验证,经过这个阶段的验证,字节流进入内存的方法区中进行存储
    3.元数据验证:
    4.字节码验证
    5.符合引用验证

准备:    
    1.准备阶段是正式为 类变量 分配内存并设置 类变量 初始值 的阶段,这些内存将在方法区中
    进行分配
    2.public static final int value = 122;
解析:
    1.解析阶段是在虚拟机将常量池内的符号引用替换为直接引用的过程
    2.符号引用
    3.直接引用
初始化:
    1.<clinit>()方法:由编译器自动收集类中所有类变量的赋值动作和静态语句块中语句合并产生,
    收集顺序是由语句在源文件中出现的顺序决定的
    2.该方法与实例构造器<init>()不同,不需要显示的调用父类构造器
    3.<clinit>()方法对于类或接口来说不是必须的
    4.执行接口的<clinit>()不需要先执行父接口的<clinit>()方法
    5.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁和同步
类的主动引用
    1.遇到new,getstatic(读取),putstatic(设置),invokestatic(调用)这四条字节码指令时
    2.使用java.lang.reflet包的方法对类进行反射调用的时候
    3.当初始化一个类的时候,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化
    4.当虚拟机启动时,虚拟机会初始化主类(包含main方法的那个类)
类的被动引用
    1.通过子类引用父类的静态字段,不会导致子类初始化
    2.通过数组定义应用类:ClassA[] array=new ClassA[10]。
    触发了一个名为ClassA的类的初始化,它是一个由虚拟机自动生成的,直接继承于object的类,创建
    动作由字节码指令newarry触发
    3.常量会在编译阶段存入调用类的常量池

九.什么是垃圾回收
    1.当一个对象没有引用指向它时,这个对象就成为无用的内存(垃圾),就必须进行回收
    以便于后续其他对象的内存分配

引用计数算法:
    实现简单,判断效率也很高,在大部分情况下它都是一个不错的算法。但是java语言
    中没有选用引用计数算法来管理内存,
    其中最主要的一个原因是它很难解决对象之间相互循环引用的问题    
    ObjA.obj = ObjB
    ObjB.obj = ObjA

1:概念:
    GcRoot是一个对象引用链的起点,引出它们指向的下一个节点,再以下个节点为起点,
    引出此节点指向的下一个结点。
    这样通过 GC Root 串成的一条线就叫引用链)直到所有的结点都遍历完毕,
    如果相关对象不在任意一个以 GC Root 为起点的引用链中,
    那么虚拟机就可以在内存不足的时候,回收 这个对象

2:GC Root对象有哪些?
虚拟机栈 -----栈帧中的本地 变量表中引用的对象
本地方法栈 -----即一般说的 Native方法引用的对象
方法区中 类静态属性引用的对象
方法区中 常量引用的对象


可达性分析算法(根搜索算法)
    1.java和C#
    2.程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,如果一个节点于GC ROOT
    之间没有引用链存在,则该节点视为垃圾回收的对象。
    3.在java语言里,可作为GC Roots对象的包括如下几种:
        1)虚拟机栈(栈帧中的本地本地变量表)中引用的对象;
        2)方法区中的类静态属性引用的对象;
        3)方法区中的常量引用的对象;
        4)本地方法栈中JNI的引用的对象;

对象引用-强引用
    1.只要引用存在,垃圾回收器永远不会回收
        Object obj = new Object();
    2.obj对象对后面new Object有一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉
对象引用-软引用
    1.非必须引用,内存溢出之前进行回收,可以通过以下代码实现
        Object obj = new Object();
        SoftReference<Object>sf = new SoftReference<Object>(obj);
        obj = null;
        sf.get();
    2.软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,
    无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,
    从真正的来源查询这些数据。
对象引用-弱引用
    1.在第二次垃圾回收时,可以通过如下代码实现
        Object obj = new Object();
        WeakReference<Object>sf = new WeakReference<Object>(obj);
        obj = null;
        wf.get();//有时候会返回null
        wf.isEnQueued();
    2.弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,
    可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器
虚引用(幽灵/幻影引用)
    1.在垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现
        Object obj = new Object();
        PhantomReference<Object>sf = new PhantomReference<Object>(obj);
        obj = null;
        pf.get();//永远返回null
        pf.isEnQueued();

分代垃圾回收的提出
    1.在java代码中,java语言没有显式的提供分配内存和删除内存的方法。
    一些开发人员将引用对象设置为null或者调用System,gc()来释放内存。
    2.在java中,由于开发人员没有在代码中显示删除内存,所以垃圾收集器会去发现不需要(垃圾)
    的对象,然后删除它们,释放内存。
    3.分代垃圾收集器是基于以下两个假设而创建的。
        1)绝大多数对象在短时间内变得不可达    
        2)只有少量年老对象引用年轻对象

年轻代和老年代
    1.年轻代:新创建的对象都存放在这里。因为大多数对象很快变的不可达,所以大多数对象在年轻代中创建
    然后消失。当对象从这块内存区域消失时,我们说发生了一次“minor GC”
    2.老年代:没有变的不可达,存活下来的年轻代对象被复制到这里。这块内存区域一般大于年轻代
    因为它更大的规模,GC发生的次数比在年轻代的少。对象从老年代消失时,我们说“major GC”或“full GC”发生
    

年轻代组成部分
    1.年轻代总共有3块空间,1块为Eden区,2块为Survivor(幸存者)区。各个空间的执行顺序如下:
        1)绝大多数新创建的对象分配在Eden区
        2)在Eden区发生一次GC后,存活的对象移到其中一个Survivor区
        3)一旦一个Survivor区已满,存活的对象移动到另外一个Survivor区
            然后之前那个空间已满的Survivor区将置为空,没有任何数据
        4)经过重复多次这样的步骤后依旧存活的对象迁移到老年代。

垃圾收集算法
Mark-Sweep(标记-清除)算法
    1.这是最基础的垃圾回收算法。分为两个阶段:标记阶段和清除阶段。
    标记阶段的任务是标记出所有需要被回收的对象
    清除阶段就是回收被标记的对象所占用的空间
    2.严重问题:容易产生内存碎片,碎片太多可能会导致后续过程中
    需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作

Copying(复制)算法
    1.为了解决Mark-Sweep(标记-清除)算法的缺陷
    它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,
    就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间清理掉,
    这样一来就不容易出现内存碎片的问题
    2.这种算法对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半
    3.与存活对象的数目多少有很大的关系,如果存活对象很多,那么算法的效率将会大大降低

Mark-Compact(标记-整理)算法
    1.为了解决Copying(复制)算法的缺陷。该算法标记阶段和Mark-Sweep一样,
    但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,
    然后清理掉端边界以外的内存

Generational Collection(分代收集) 算法
    1.该算法是目前大部分JVM的垃圾收集器采用的算法。
    它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。
    一般情况下将堆区划分为老年代和新生代,
    老年代的特点是每次垃圾收集时只有少量对象需要被回收
    新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特定采取最适合的收集算法
    2.目前大部分垃圾收集器对于新生代都采用Copying算法
    3.由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法
    4.注意,在堆区之外还有一个代就是永久代,它用来存储class类,常量,方法描述等。
    对永久代的回收主要回收两部分内容:废弃常量和无用的类。

十.垃圾收集器
Serial/Serial Old
    1.Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器
    并且在它进行垃圾收集时,必须暂停所有用户线程。
    Serial收集器是针对新年代的收集器,采用Copying算法
    Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法
    它的优点是实现简单高效,但是缺点是会给用户带来停顿

ParNew
    ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集

Parallel Scavenge
    1.Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),
    它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同
    它主要是为了达到一个可控的吞吐量
    2.Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法

CMS
    1.CMS收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法

G1
    1.G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能
    充分利用多CPU,多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型
    2.Layout of G1 GC


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

掉帧且闪退

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值