带你走进java的JVM

首先在学习之前我们需要先看看在JVM中的重点内容都有哪些:
1.java内存区域与内存溢出异常
2.垃圾回收器与内存分配策略中怎么判断对象已死、回收
3.java内存模型
一、JVM简介
二、java内存区域与内存溢出异常(重点)
1.运行时数据区域
(特别重要,须记住)
线程私有区域:程序计数器、java虚拟机栈、本地方法栈
线程共享区域:java堆、方法区、运行时常量池
下面我们来看看它们都是干嘛用的:
(1)线程私有区域:
1)程序计数器:用来记录程序每次执行的位置。程序计数器每个线程都要有一个独立的。
2)java虚拟机栈
局部变量表:存放了编译器可知的各种基本数据类型(8大基本数据类型)、对象引用。
3)本地方法栈:它只针对一个点,只对Native方法。并且它只针对于HotSpot虚拟机。
(2)线程共享区域:
1)java堆:是JVM所管理的最大内存区域。此内存区域存放:所有的对象实例以及数组。
2)方法区:用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。在JDK1.8之前被叫做:永久代(记住),后面叫做:Meta Space。在存放的时候如果内存不够了将会抛出OOM异常。
3)运行时常量池:JDK1.7之前,它是方法区的一部分;JDK1.7之后挪到了堆上。
2.java堆溢出
内存溢出:内存不够了。如何解决这个问题:调整堆、对象的生命周期是否太长了。
内存泄漏:对象没有办法被回收
我们在编写程序的时候,不要出现内存泄漏的现象,因为我们的空间可能没有被使用,然后就没有释放空间造成的内存泄漏。那么我们应该怎么来解决这个问题呢?即时关闭流、即时关闭文件、即时将不用的对象变为nul、尽可能的在使用的时候创建对象,在没有使用的时候先不要创建对象。
3.虚拟机栈和本地方法栈
目前它们两个已经合二为一了。但是他并不代表以后的情况,我们的虚拟机不像语法,不需要前后兼容。
如果我们的栈一直在进栈,但是没有出栈的话,我们的栈就会很快的溢出。
虚拟机栈会产生的两种异常:
(1)调用的深度大于我们的虚拟机本身的深度就会抛出栈溢出的异常。
(2)如果空间申请不到就抛出OOM。
调整栈的空间大小,观察异常:

package com.wschase.jvm;

/**栈溢出
 * Author:WSChase
 * Created:2019/1/21
 */
public class TestStack {
    
    public int stackDepth=1;
    
    //1.典型的递归调用
    //2.递归无出口
    public void setStack(){
        this.stackDepth++;
        this.setStack();
    }

    public static void main(String[] args) {
        TestStack testStack=new TestStack();
        try{
            testStack.setStack();
        }catch (Throwable e){
            System.out.println("异常信息:"+e.getMessage());
        }
        System.out.println("调用深度:"+testStack.stackDepth);
    }
}

三、垃圾回收器与内存分配策略(重点)
1.之所以有垃圾回收,是因为对象在堆上存放着,堆的内存是有限的。
垃圾回收我们需要解决三个问题:
(1)什么时候回收
(2)什么样的对象可以回收
(3)我们应该怎么回收
2.垃圾回收的对象
对于线程私有的程序计数器、虚拟机栈、本地方法栈,它们的生命周期与线程相关,随着线程生而生、死而死。所以我们的垃圾回收的对象是java堆与方法区两部分。
3.对于垃圾回收的三个问题的解决
*(1)什么时候回收:当我们的回收对象没有被引用的时候,已经死了的时候就表示垃圾了,这个时候就需要被回收。
*(2)回收谁——回收已死的对象——如何判断它怎么死的
没有人再引用它(怎么判断):
1)引用计数法
对于有引用的就计数一次,当我们发现引用的次数为0时就是没有人引用它,那么这个时候就是已经死了的对象需要回收。
2)可达性分析法
从一个对象出发,看这个对象可不可以到达gc Root对象,如果可以到达那么这个对象就不是死的,如果这个对象不可以到达gc Root那么就是已经死了的对象,是可以回收的对象。
(a) 在java中可以作为gc Root对象的:
a.虚拟机栈(栈帧中的本地变量表)中引用的对象 b. 方法区中类静态属性引用的对象
c. 方法区中常量引用的对象
d.本地方法栈中JNI(Native方法)引用的对象
在JDK1.2以后,java对引用的概念做了扩充,将引用分为:强引用、软引用(二次回收)、弱引用(一次回收)、虚引用,它们的引用强度依次递减。
(b)生存与死亡
在这里插入图片描述
3)回收方法区
方法区回收的两部分主要是:废弃的常量和无用的类。
1)判定一个类是无用类
a.这个类所有的实例都已经被回收
b.加载该类的ClassLoader已经被回收
c.无反射调用
*(3)我们应该怎么回收——垃圾回收算法
1)标记-清除算法
缺点:
碎片太多——不连续
效率不高
2)复制算法(新生代回收算法)
会将内存划分为三个块,一个叫新生块,两个幸存者。
在这里插入图片描述
3)标记-整理算法(老年代回收算法)
4)分代收集算法
根据我们对象存活的地方来划分的。
一个小问题
Minor GC:是新生代GC;发生在新生代的垃圾收集。
Full GC:老年代GC;发生在老年代的垃圾收集。
4.垃圾收集器
总共有7个垃圾回收器:
在这里插入图片描述
它们之间有连线表示连线的垃圾回收器可以配合使用。
首先我们需要知道三个概念:
*并行:在多核CPU上,用户线程与垃圾收集线程同时执行,用户程序继续运行。
*并发:指多条线程并行工作,用户线程处于等待状态。
*吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
我们衡量一个程序的运行效率经常用吞吐量。
下面我们分别介绍这几种垃圾回收器:
(1)Serial收集器(新生代收集器,串行GC)
我在进行垃圾回收的时候就会停止我们的工作程序。
目前我们虚拟机默认的是server模式
(2)ParNew收集器(新生代收集器,串行GC)
相比于Serial收集器,它是多线程收集器。场景是server模式。
(3)Parallel Scavenge收集器(新生代收集器,并行GC)
它可以控制吞吐量,使用的是复制算法,并行的多线程收集器。
应用场景:要有良好的用户体验使用这个收集器。
(4)Serail Old收集器(老年代收集器,串行GC)
是一个单线程收集器,使用标记-整理算法
(5)Parallel Old收集器(老年收集器,并行GC)
(6)CMS收集器(老年代收集器,并发GC)
以获取最短回收停顿时间为目标的收集器。
(7)G1收集器(唯一一款全区域的垃圾回收器)
G1垃圾回收器在清除实例所占用的内存空间后,还会做内存压缩。
垃圾回收器总结:
最后我们的新生代和老年代都到达了多线程的使用,这样我们的吞吐量才能达到最大。
在这里插入图片描述5.内存分配与回收策略
(1)对象有限再Eden分配
(2)大对象直接进入老年代
什么是大对象:就是本来给分配的内存小于了实际对象的内存大小,那么这个对象就叫大对象,把大对象直接放到老年代。
(3)长期存活对象直接进入老年代
怎么来判断是长期存活的对象:虚拟机给每个对象定义一个对象的年龄。
(4)动态对象年龄的判断
它无需等待年龄等到一个具体的年龄才能放到老年代。直接有一个规定,占一半的年龄是一个界限,当大于这个年龄的就将它放到老年代,比较灵活。
(5)空间分配担保
在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。
如果大于,则此次Minor GC是安全的;
如果小于,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。
四、常用JVM性能监控与故障处理工具(了解)
1.JDK命令行工具
2.jps:虚拟机进程状态工具
jps -q:只输出LVMID,省略主类的名称
jps -m:输出虚拟机进程启动时传递给主类main()函数的参数
jps -l:输出主类的全名,如果进程执行的是jar,输出jar路径
jps -v: 输出虚拟机进程启动时JVM参数
3.jstat:虚拟机统计信息监视工具
jstat -[option]:这个选项主要分为三类:类装卸、垃圾收集、运行期编译等状况。
4.jinfo:查看和调整虚拟机的配置参数
5.jmap:java内存映像工具
6.jhat:虚拟机转存储快照分析工具
7.jstack:java堆栈跟踪工具
五、java内存模型(重点)
java系统屏蔽了硬件和操作系统之间的交互,为了保证我们的跨平台,它的内存模型不像C++内存模型,不同的数据类型在不同平台上的长度还不一样,我们在java中的基本数据类型不管在哪个平台下都是一样的,这就是因为我们定义的一套内存模型。我们的内存模型是java自定义的,在每个平台上我们不会因为平台差异导致错误。
1.主内存和工作内存
主内存:操作系统内存
工作内存:创建一个变量它要有自己独有的一个区域,这时候这个区域就叫做工作内存
我们在java中进行读、写不会直接在操作系统的内存进行操作,而是在我们的工作内存进行操作,最后才从工作内存转到操作系统内存。
2.内存间交互操作——它们都是原子操作
(1)锁定:如果我们要去读取信息,这个信息在主内存里面,那么我们就会将主内存锁定。
(2)解锁:还是在主内存,解锁就是我要将我独占的这个锁释放掉。
(3)读取:我们要将主内存的变量读到我们的工作内存。这个操作也是作用到我们的主内存。
(4)载入:它作用于我们的工作内存。
(5)使用:作用于我们的工作内存。
(6)赋值:作用于工作内存
(7)存储:作用于工作内存,它将工作内存的变量的传送到主内存,便于后面的写入。
(8)写入:作用于主内存,将变量从工作内存送到主内存中。
3.java内存模型的三大特性:
(1)原子性:原子性指的就是我们上面的8个命令。并且基本数据的访问具备原子性。
(2)可见性:指当一个线程修改了共享变量的值,其他线程能够立即得知这个线程的修改。volatile、synchronized、final这三个可以实现可见性。
(3)有序性:对线程内部来说是有序的(线程内变现为串行),对线程外来说是无序的(指令重排序)。
先行发生原则(happens-before)
(1)程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
(2)锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作
(3)volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
(4)传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
(5)线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
(6)线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、 Thread.isAlive()的返回值手段检测到线程已经终止执行
(7)对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
4.volatile型变量的特殊规则(务必记住)
如果一个变量被volatile修饰以后,它应该具备有以下两个特性:
(1)保证此变量对所有的线程可见。
这里的可见指的是如果你有一个线程是修改一个由volatile修饰的变量,把这个变量一修改,所有的访问这个变量的线程立马获得你修改以后的值。我们的普通变量是做不到的,普通变量如果要能一修改其他线程就立马获得是需要经过这5步骤的:修改完后要stop–>write–>read–>load–>use。
++:不是原子操作----->不是原子操作的运算它的结果是不确定的。
注意:
volatile修饰的变量是多线程可见的
volatile修饰的变量进行运算,不保证原子性。(就是它修饰的变量参加运算的结果是不确定的)
(2)使用volatile变量的语义是禁止指令重排序的
volatile关键字禁止指令重排序有两层意思:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经 对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
六、对象深浅拷贝(只要知道深拷贝和浅拷贝之间的区别就可以了)
要想让对象具有具有拷贝功能,就必须让这个对象实现Cloneable接口。
1.深拷贝和浅拷贝之间的区别
浅拷贝:只是拷贝了对象的一些属性信息,但是对于对象里面引用的属性对象并没有拷贝。它存在的问题是:两个对象之间还是存在一定的关系。
深拷贝:拷贝出来的对象产生了所有引用的新对象。
2.深拷贝和浅拷贝的代码实现
(1)浅拷贝:

package com.wschase.qiankaobei;

/**注意:
 * 一个类的实例化对象要能够克隆得实现Cloneable接口
 * Author:WSChase
 * Created:2019/1/22
 */
public class Student implements Cloneable{
    private String name;

    private Integer age;

    private Teacher teacher;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    protected Student clone() throws CloneNotSupportedException {
        Student student=null;
        student=(Student)super.clone();
        return student;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", teacher=" + teacher +
                '}';
    }

    public Student(String name, Integer age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }
}

package com.wschase.qiankaobei;

/**
 * Author:WSChase
 * Created:2019/1/22
 */
public class Teacher {
    private String nane;

    private String direction;

    public String getNane() {
        return nane;
    }

    public void setNane(String nane) {
        this.nane = nane;
    }

    public String getDirection() {
        return direction;
    }

    public void setDirection(String direction) {
        this.direction = direction;
    }

    public Teacher(String nane, String direction) {
        this.nane = nane;
        this.direction = direction;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "nane='" + nane + '\'' +
                ", direction='" + direction + '\'' +
                '}';
    }
}

package com.wschase.qiankaobei;

/**浅拷贝
 * Author:WSChase
 * Created:2019/1/22
 */
public class TestClone1 {

    public static void main(String[] args) {
        Teacher teacher=new Teacher("Peter","C");
        Student student=new Student("Jack",22,teacher);

        System.out.println("这是原Student");
        System.out.println(student);

        System.out.println("这是Clone的Student");
        try {
             Student cloneStudent = student.clone();
        System.out.println(cloneStudent);



            teacher.setNane("Tang");
            System.out.println("这是原Student");
            System.out.println(student);

            System.out.println("这是Clone的Student");
            System.out.println(cloneStudent);

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }


        //注意:
        //teacher ->student
        //teacher ->cloneStudent

        //1.如果teacher的信息改变了,student和cloneStudent中关于teacher的信息都改变,说明teacher是共享的
        //2.如果teacher的信息改变了,student中关于teacher的信息都改变,cloneStudent中关于teacher的信息都未改变,
        // 说明teacher不是共享的

    }
}

(2)深拷贝:

package com.wschase.shenkeobei;

import java.io.*;

/**
 * Author:WSChase
 * Created:2019/1/22
 */
public class Student implements Cloneable,Serializable {
    private String name;

    private Integer age;

    private Teacher teacher;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }


    //浅拷贝
    @Override
    protected Student clone() throws CloneNotSupportedException {
        Student student=null;
        student=(Student)super.clone();
        return student;
    }

    //深拷贝
    public Student cloneStudent(){

        try(ByteArrayOutputStream out=new ByteArrayOutputStream();
            ObjectOutputStream objOut = new ObjectOutputStream(out);
            ) {
            //写入
            objOut.writeObject(this);//this->student对象
            try (
                    ByteArrayInputStream input=new ByteArrayInputStream(out.toByteArray());
                    ObjectInputStream objInput = new ObjectInputStream(input)) {

                //读取
                Student student = (Student) objInput.readObject();
                return student;
            }catch (IOException ignored){
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", teacher=" + teacher +
                '}';
    }

    public Student(String name, Integer age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }
}

/**
 * Author:WSChase
 * Created:2019/1/22
 */
public class Teacher implements Serializable {
    private String nane;

    private String direction;

    public String getNane() {
        return nane;
    }

    public void setNane(String nane) {
        this.nane = nane;
    }

    public String getDirection() {
        return direction;
    }

    public void setDirection(String direction) {
        this.direction = direction;
    }

    public Teacher(String nane, String direction) {
        this.nane = nane;
        this.direction = direction;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "nane='" + nane + '\'' +
                ", direction='" + direction + '\'' +
                '}';
    }
}


package com.wschase.shenkeobei;

/**深拷贝
 * Author:WSChase
 * Created:2019/1/22
 */
public class Clone2 {

    public static void main(String[] args) {
        Teacher teacher=new Teacher("Peter","C");
        Student student=new Student("Jack",22,teacher);

        System.out.println("这是原Student");
        System.out.println(student);

        System.out.println("这是Clone的Student");
            Student cloneStudent = student.cloneStudent();
            if (cloneStudent!=null){

            System.out.println(cloneStudent);

            teacher.setNane("Tang");
            System.out.println("这是原Student");
            System.out.println(student);

            System.out.println("这是Clone的Student");
            System.out.println(cloneStudent);
            }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值