Jvm原理和优化

来源:互联网 发布:java实现支付宝转账 编辑:程序博客网 时间:2024/06/16 19:27

类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

加载

就是指将class文件读入内存,并为之创建一个Class对象。

任何类被使用时系统都会建立一个Class对象。

连接

验证 是否有正确的内部结构,并和其他类协调一致

准备 负责为类的静态成员分配内存,并设置默认初始化值

解析 将类的二进制数据中的符号引用替换为直接引用

初始化 通过new来实例化一个对象


JVM的类加载是通过ClassLoader及其子类来完成的

类初始化步骤:

1. 在一个类被初始化之前,也就是在任何非字面类常量的类字段被初始化之前,字面类常量先完成初始化,如字段staticfinalStirng a ="good"

2. 当类被触发进行初始化,若其直接父类还没有被初始化,先对直接父类进行初始化;若直接父类的直接父类没有被初始化,则先对直接父类的直接父类进行初始化,以此类推,直到Object类或某一级别的祖父类已被初始化。

3. 初始化所有非类常量的类字段,同时初始化静态代码块,按文本序。 


类加载器

Bootstrap ClassLoader根类加载器

  也被称为引导类加载器,负责Java核心类的加载

  比如System,String等。在JDK中JRE的lib目录下  rt.jar文件中

Extension ClassLoader扩展类加载器

  负责JRE的扩展目录中jar包的加载。

  在JDK中JRE的lib目录下ext目录

System ClassLoader系统类加载器

  负责在JVM启动时加载来自java命令的class文件,以及  classpath环境变量所指定的jar包和类路径


类加载器顺序


1、虚拟机在首次加载Java类时,会对静态初始化块、静态成员变量、静态方法进行一次初始化

 2、只有在调用new方法时才会创建类的实例

 3、类实例创建过程:按照父子继承关系进行初始化,首先执行父类的初始化块部分,然后是父类的构造方法;再执行本类继承的子类的初始化块,最后是子类的构造方法

 4、类实例销毁时候,首先销毁子类部分,再销毁父类部分


public class Parent { 

public static int t = parentStaticMethod2(); //   静态成员变量第二个初始化
public static final int x=parentStaticMethod3(); //父类类常量在类初始化前会第一个初始化
{ System.out.println("父类非静态初始化块"); }           //第五个初始化
static { System.out.println("父类静态初始化块"); }     //第三个初始化
  public Parent() { 
System.out.println("父类的构造方法");             //第六个初始化

public static int parentStaticMethod() {
System.out.println("父类类的静态方法"); 
return 10;
 } 
public static int parentStaticMethod2() { 
System.out.println("父类的静态方法2"); 
return 9;
 } 
public static int parentStaticMethod3() { 
System.out.println("父类的静态方法3"); 
return 9;
 } 
}


public class Child extends Parent{
    { System.out.println("子类非静态初始化块");}   //第七个初始化
    static {
        System.out.println("子类静态初始化块");   //第四个初始化
     }
    public Child() {
        System.out.println("子类的构造方法");      //第八个初始化
    }
    public static int childStaticMethod() {
        System.out.println("子类的静态方法");
        return 1000;
    }
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("销毁子类");
    }
}


public class Test{   
    public static void main(String[] args)
    {
        // TODO Auto-generated method stub
      //  Parent.parentStaticMethod();
   Child child = new Child();
        System.out.println("子类初始化完成"); 
    }


}


控制台结果结果如下:

父类的静态方法2
父类的静态方法3
父类静态初始化块
子类静态初始化块
父类非静态初始化块
父类的构造方法
子类非静态初始化块
子类的构造方法
子类初始化完成


JVM垃圾回收GarbageCollection(GC)


JVM的垃圾回收

  JVM的垃圾回收指的是回收JVM内存堆内的数据对象

堆是什么

  JVM堆内存储所有new、newarray、anewarray和  multianewarray等指令建立的对象。

回收是什么

  一种动态存储管理技术,它自动地释放不再被程序引用的对象,  按照特定的垃圾收集算法来实现资源自动回收的功能。

GC的优点:


在C++中,对象在被程序结束或被明确的释放前不能被回收。

提高编码效率,不用花费大量时间考虑内存分配和对象的生命周期


GC的缺点:


不能控制对象的生命结束,意味着逻辑不能卸载析构函数。

在运行GC程序时,JVM会停止运行,对程序效率产生影响,潜在的加大了程序的负担


内存管理:如图


内存--堆

所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,Survivor由FromSpace和ToSpace组成,结构图如下所示:



内存--栈

每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果。

本地方法栈

用于支持native方法的执行,存储了每个native方法调用的状态。

方法区

存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(或者叫做永久代,PermanetGeneration)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值


对象的状态转换如图


当一个对象(假定为Sample对象)被创建后,只要程序中还有引用变量引用它,那么它就始终处于可触及状态

当Java虚拟机执行完所有可复活对象的finalize()方法后,假如这些方法都没有使Sample对象转到可触及状态,那么Sample对象就进入不可触及状态。只有当对象处于不可触及状态时,垃圾回收器才会真正回收它占用的内存。


透视垃圾回收 


1、命令行参数透视垃圾收集器的运行

2、使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。在命令行中有一个参数-verbosegc可以查看Java使用的堆内存的情况,它的格式如下:

java-verbosegc classfile
 

(1)不要试图去假定垃圾收集发生的时间,这一切都是未知的。

(2)Java中提供了一些和垃圾收集打交道的类,而且提供了一种强行执行垃圾收集的方法--调用System.gc(),但这同样是个不确定的方法。。  

(3)挑选适合自己的垃圾收集器。一般来说,如果系统没有特殊和苛刻的性能要求,可以采用JVM的缺省选项。

(4)关键的也是难把握的问题是内存泄漏。良好的编程习惯和严谨的编程态度永远是最重要的,

(5)尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为null,暗示垃圾收集器来收集该对象,还必须注意该引用的对象是否被监听,如果有,则要去掉监听器,然后再赋空值。