jvm内存模型及类加载器

jvm执行程序过程

  • 加载.class文件
  • 管理并分配内存
  • 垃圾回收

类加载过程

这里写图片描述

  • 加载:将字节码的静态数据结构加载到方法区形成运行时数据结构,并在堆中生成这个类的class对象,作为方法区数据的访问入口
  • 链接:
    • 验证:确保加载的类信息符合jvm规范,不会危害虚拟机的安全。
    • 准备:仅为类的静态变量(static filed)在方法区分配内存,并赋默认初值(0值或null值)。如static int a = 100;静态变量a就会在准备阶段被赋默认值0。而静态常量(static final filed)会在准备阶段赋程序设定的初值。
    • 解析: 将常量池的符号引用替换为直接引用的过程。(A.a = “Hello”替换为A.a指向“Hello”的地址)
  • 初始化:先递归初始化父类。执行类构造器clinit()方法,合并静态变量与静态语句块。(虚拟机保障静态变量静态块的线程安全)
    • Java虚拟机规范没有强制性约束在什么时候开始类加载过程,但是对于初始化阶段,虚拟机规范则严格规定主动引用一定会发生初始化,被动引用不会发生初始化

主动引用与被动引用

  • 主动引用
    • new()
    • 调用静态但非常量成员及方法
    • 反射调用
    • main()所在类
    • 父类
  • 被动引用
    • 引用常量池
    • 通过数组定义 A[] A=new A[10];
    • 访问一个静态变量时,只有真正声明这个变量的类会被初始化。例如子类B extends A,A中有static int a;那么B.a,B不会被初始化

jvm内存模型

这里写图片描述

运行时数据区的一篇文章

  • PC:线程私有,为了线程切换后能恢复到正确的执行位置。如果线程执行的是Java 方法,计数器记录正在执行的虚拟机字节码指令地址;如果执行的是Natvie 方法,计数器值为空(Undefined)。

  • 虚拟机栈:线程私有,生命周期与线程相同。每个方法被执行时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息. 每个方法被调用至返回的过程, 就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程(VM提供了-Xss来指定线程的最大栈空间, 该参数也直接决定了函数调用的最大深度)

  • 本地方法栈:线程私有,为虚拟机使用到的Native 方法服务。

  • :存放对象实例,被所有线程共享,也是垃圾收集器管理的主要区域。堆的大小可以通过-Xms(最小值)和-Xmx(最大值)参数设置,-Xms为JVM启动时申请的最小内存,默认为操作系统物理内存的1/64但小于1G,-Xmx为JVM可申请的最大内存,默认为物理内存的1/4但小于1G,默认当空余堆内存小于40%时,JVM会增大Heap到-Xmx指定的大小,可通过-XX:MinHeapFreeRation=来指定这个比列;当空余堆内存大于70%时,JVM会减小heap的大小到-Xms指定的大小,可通过XX:MaxHeapFreeRation=来指定这个比列,对于运行系统,为避免在运行时频繁调整Heap的大小,通常-Xms与-Xmx的值设成一样

  • 方法区:被所有线程共享,线程安全。用于存储常量池,类相关信息(类型信息,字段信息,方法信息,静态变量),指向ClassLoader的指针,指向Class对象的指针,方法表等。

垃圾回收

  • 标记-清除算法
    这里写图片描述
  • 复制算法
    这里写图片描述
  • 标记-整理算法
    这里写图片描述

只有方法区和堆需要进行GC。
分代回收算法:内存被分为新生代、旧生代、持久代、他们所用的GC算法不同。
这里写图片描述

  • 新生代:新生代由Eden Space和两块相同大小的Survivor Space构成,默认比例为8:1:1,可通过-Xmn参数来指定新生代的大小,也可以通过-XX:SurvivorRation来调整Eden Space及SurvivorSpace的大小。
    对象首先在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到旧生代。
    虚拟机对新生代的垃圾回收称为Minor GC,次数比较频繁,每次回收时间也比较短。新生代使用复制算法标记-清除垃圾收集算法

  • 旧生代:旧生代中存放生命周期较长的对象,例如缓存对象,新建的对象也有可能直接进入旧生代,主要有两种情况:1、大对象,可通过启动参数设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在旧生代分配。2、大的数组对象,且数组中无引用外部对象。旧生代所占的内存大小为-Xmx对应的值减去-Xmn对应的值。旧生代采用标记-整理垃圾回收算法。虚拟机对年老代的垃圾回收称为MajorGC/Full GC,次数相对比较少,每次回收的时间也比较长。

  • 永久代:永久代垃圾回收比较少,效率也比较低,也使用标记-整理算法进行垃圾回收,java虚拟机参数-XX:PermSize和-XX:MaxPermSize可以设置永久代的初始大小和最大容量。

类加载器

层次结构:

类加载器之间的父子关系通过组合实现。
这里写图片描述

  • Bootstrap ClassLoader:负责加载Java的核心类库,%JAVA_HOME%/lib)目录下的rt.jar(包含System、String这样的核心类)。它由C/C++实现,不是java.lang.ClassLoader的子类。

classloader相关方法:

 ClassLoader getParent()  
 Class<?> loadClass(String name) 
 Class<?> findClass(String name)  
 Class<?> defineClass(String name, byte[] b, int off, int len) 
 void resolveClass(Class<?> c) 

双亲委派机制

  • 保证java核心类库的类型安全
  • 1、”A类加载器”加载类时,先判断该类是否已经加载过了;
    2、如果还未被加载,则首先委托其”父类加载器”去加载该类,这是一个向上不断搜索的过程,当A类所有的”父类加载器”(包括bootstrap classloader)都没有加载该类,则回到发起者”A类加载器”去加载;
    3、如果还加载不了,则抛出ClassNotFoundException。
   protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }`

自定义类加载器

class MyClassLoader extends ClassLoader{


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> c=findLoadedClass(name);
        if(c!=null){
            return c;
        }else{
            ClassLoader parent = this.getParent();
            try {
                c = parent.loadClass(name);
            } catch (Exception e) {

            }
            if(c!=null){
                return c;
            }else{
                byte[] classdata=getClassData(name);
                if(classdata==null){
                    throw new ClassNotFoundException();
                }else{                  
                    c=defineClass(name,classdata, 0, classdata.length);
                }
            }
        }
        return c;
    }

    private byte[] getClassData(String name) {
        InputStream in=null;
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        try {
            in= new FileInputStream(name);
            byte[] buffer=new byte[1024];
            int temp=0;
            while((temp=in.read(buffer))!=-1){
                baos.write(buffer,0,temp);
            }
            return baos.toByteArray();
        } catch (FileNotFoundException e) {

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

            e.printStackTrace();
        } finally{
            if(in!=null){
                try {

                    in.close();
                } catch (IOException e) {

                    e.printStackTrace();
                }

            }
        }
        return null;
    }

}

类相等的判定条件

  • 两个类来自同一个Class文件
  • 两个类是由同一个虚拟机加载
  • 两个类是由同一个类加载器加载

线程上下文加载器:

Thread.currentThread().setContextClassLoader(new MyClassLoader());  

参考文章1-JVM内存模型
参考文章2-JVM内存模型
参考文章3-JVM垃圾回收机制
参考文章4-JVM运行时数据区
参考文章5-JVM类加载器机制与类加载过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值