JVM概述和类加载器

JVM概述
1.Java虚拟机所管理的内存包括以下几个运行时数据区域:
  ①.程序计数器
    程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值来选取下一个要执行的字节码指令,分支、循环、跳转、异常处理都需要依赖于这个计数器来完成;
    Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来来实现,在任何一个时刻、一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每个线程都需要一个独立的程序计数器。
 
  ②.Java虚拟机栈(主要存储 8种基本数据类型 + 对象的引用 + 实例方法)
     Java虚拟机栈的局部变量表存放了编译期可预知的各种基本数据类型(八种);栈主管程序的运行,生命周期跟随线程的生命周期;
     在虚拟机中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,对抛出StacKOverFlowErroe异常;如果虚拟机栈在动态扩展时,如果无法申请到足够的内存,会抛出OOM异常
 
  ③.本地方法栈
    本地方法栈和虚拟机栈的作用相似,区别是:虚拟机栈是为虚拟机执行Java方法服务的,本地方法栈是为虚拟机使用到native方法服务
    
  ④.Java堆
    Java堆(Java heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程所共享的一块内存区域,该内存区域是用来存放所有的对象实例,所以对象实例都是在这里分配内存。
    Java堆是垃圾收集器管理的主要区域;如果在堆中没有完成内存实例的分配,并且堆也无法扩展时,会抛出OutOfMemory的异常
    Eden + survivor0 _ suvivor1 = 新生代
    新生代 + 老年代 = 堆
    逻辑上分为 新生区 + 养老区 + 元空间
    物理上分为 新生区 + 养老区
 
  ⑤.方法区(Non-Heap)(存储静态变量 + 常量 + 类信息(构造方法和接口定义) + 运行时常量池存在方法区中)
    方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;
 
  ⑥.运行时常量池
  ⑦.直接内存
 
 类加载器
 
1.类加载器是干什么的?
 ClassLoader:根据一个指定的类的名称,找到或者生成对应的字节码,从这些字节码中定义出一个java类,java.lang.Class类的对象
 
2.类装载器ClassLoader有四种:
 Bootstrap loader 启动类加载器(负责加载java的核心类库,比如:rt.jar)
 扩展类加载器 ExtensionClassLoader(加载java的扩展库)
 应用程序类加载器,也叫系统类加载器 appClassLoader,负责加载当前classpath的所有类
 用户自定义加载器:java.lang.ClassLoader的子类
   
3.类的加载时机:
 创建类的实例 new一个对象的时候
 访问某个类或接口的静态变量,或者对该静态变量赋值
 调用类的静态方法
 反射 Class.forName()
 
4.双亲委派机制:
 类加载器是根据指定全类名将Class文件加载到JVM内存,转为Class对象
 双亲委派是指:如果一个类加载器收到类加载的请求,首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器完成;只有当父类加载器在自己的搜索范围找不到指定的类时,子类加载器才会尝试去加载
 
双亲委派机制的优点:
 这种机制的好处就是:如果有人想要替换系统级别的类,比如:String.java ;如果类加载器直接加载了,就有可能会运行被篡改过的String类,双亲委派机制,保证了永远是最顶端的类加载器加载String类;保护java核心库的安全性
 
针对java.*开头的类,JVM的实现中保证了必须由bootstrap来加载
在自定义的类加载器里面强制加载自定义的java.lang.String类,这样是不可行。在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。
 
双亲委派机制是自己不加载,先向上传递
代理模式正好和双亲委派机制相反,代理模式是自己先加载,如果无法加载,就向上传递,Tomcat是典型的代理机制
posted @ 2019-06-20 16:24  小小少年-  阅读(205)  评论(0编辑  收藏  举报