JVM基础

  最近学习了JVM,知道得越多,才知道自己越不足,现在进行总结一下哈。

  (1)什么是虚拟机?

    相信学习JAVA的人都知道因为虚拟机JVM的存在,才使得java程序可以跨平台,跨操作系统进行运行。首先在D盘新建一个test.java,代码如下:

public class test {
    public static void main(String[]args) throws InterruptedException{
    Thread.sleep(10000000);
    }
}

    打开cmd控制台,执行javac test.java进行编译,然后再执行java test,打开进程管理器,如下:

    

    启动eclipse,同样新建一个test类,内容如上,执行run as java application,查看进程,如下:

    

    java.exe,javaw.exe 就是java虚拟机了。java虚拟机相当于java类,而java虚拟机实例,相当于我们new一个java类得到的对象,不过java虚拟机不是通

new这个关键字而是通过java.exe或者javaw.exe来启动一个虚拟机实例。

  (2)虚拟机的生命周期

    java虚拟机中有两种线程,一种是守护线程,典型的就是gc线程,一种是非守护线程。java虚拟机中,只要有非守护线程存在,虚拟机实例就不会退出,也就

是说只有当程序中所有非守护线程执行结束,虚拟机的生命周期才会结束。下面写个简单的例子:

package test;

public class testDemo {

    public static void main(String []args) throws InterruptedException{
        new Thread(new Runnable() {
            public void run() {
                for(int i=1;i<3;i++){
                    try {
                        Thread.sleep(i*100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        
        for(int i=0;i<20;i++){
            System.out.print(i+",");
        }
    }
}

    console 输出如下:

    

    当主线程执行完后,由于程序中开启的另外一条线程还没有执行完成,所以java虚拟机一直运行着。

  (3)虚拟机的基本结构

    

    (1)类加载子系统:负责从硬盘中加载Class信息,然后将类的信息存放在方法区中。例如,当ClassLoader启动的时候,首先到硬盘中test.class装载到jvm的方法区中,方法区中的这个字节码会被虚拟机执行new操作,然后在堆中生成一个test对象。

    (2)方法区:或称永久区,类似我们电脑中的硬盘,存放类的一些信息,类信息,字段信息,方法信息,指向对象的引用等等,新建一个类,你就知道方法区存放类的哪些信息了,如下:

package test;

import java.io.Serializable;

//1.类信息
public final class testDemo implements Serializable {

     private static final long serialVersionUID = 1L;
     
     //2.对象字段信息
     private String name;
     private int id;
     
     //4.常量池
     public final int CONST_INT=0;
     
     //5.类变量区
      public static String static_str="static_str";
      
     //3.方法信息
     public String add(){
         return "jvm";
     }
     
     public String getName() {
        return name;
     }
     public void setName(String name) {
        this.name = name;
     }
     public int getId() {
        return id;
     }
     public void setId(int id) {
        this.id = id;
     }

}

     (3)java栈:每一个java线程都有一个私有的java栈,存放着局部变量以及java堆中对象的引用变量。

     (4)java堆:new出来的对象都存放在java堆中,堆空间是所有线程共享的。

     (5)垃圾回收系统:gc主要是对java堆,方法区,直接内存进行垃圾回收,释放内存。

     (6)直接内存:直接内存是在java堆外的、直接向系统申请的内存空间。通常访问直接内存的速度会优于java堆。因此,读写频繁的场合可能会考虑使用直接内存。由于直接内存在java堆外,因此它的大小不会直接受限于Xmx指定的最大堆大小,但是系统内存是有限的,java堆和直接内存的总和依然受限于操作系统能给出的最大内存。

    (7)pc寄存器:pc寄存器是用于存放下一条将要执行的指令的地址。

    (8)执行引擎:负责执行虚拟机的字节码。

  (4)java堆结构

    

    java堆整体分两个部分,即新生代和老年代。新生代主要存放新生对象,老年代主要存放时间比较久的对象,也就是多次GC后还存活的对象。新生代又可以分为eden(伊甸区)和幸存区(s0和s1)。

    对象刚创建的时候,一般都存放在eden区,除非对象特别大,直接存放在老年代。如果eden区的对象在经历过gc后还存活,则进入s0或s1区。s0和s1是两个大小,结构完全一样的内存空间,每次都知使用一个。在新生代区,垃圾回收机制一般使用的是复制算法。比如,当创建两个对象A,B后,首先进入eden区,在经历过gc后,存活的对象进入幸存区s0或s1,这里假设A,B都存活且进入了s0。下次进行gc后,假设A没有被引用了,B还存活,这时,复制算法,首先将s0中存活的对象,即B对象复制到幸存区s1,然后将s0清空。下次gc又将s1区中存活的对象复制到s0中,然后清空s1,以此类推。经过多次gc后还存活的话,则进入了老年代。

  参考文档:http://m.blog.csdn.net/article/details?id=8289363

原文地址:https://www.cnblogs.com/gdpuzxs/p/6919270.html