精华内容
下载资源
问答
  • jvm工作原理

    2016-10-08 16:42:04
    JVM工作原理

    (新人第一次发帖)

    一、         JVM的生命周期

    1.       JVM实例对应了一个独立运行的java程序它是进程级别

    a)     启动。启动一个Java程序时,一个JVM实例就产生了,任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点

    b)     运行。main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以标明自己创建的线程是守护线程

    c)     消亡。当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出

    2.       JVM执行引擎实例则对应了属于用户运行程序的线程它是线程级别的

     

    二、         JVM的体系结构

     

    1.       类装载器(ClassLoader)(用来装载.class文件)

    2.       执行引擎(执行字节码,或者执行本地方法)

    3.       运行时数据区(方法区、堆、java栈、PC寄存器、本地方法栈)

     

    三、         JVM类加载器

    JVM整个类加载过程的步骤:

    1.       装载

    装载过程负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名通过ClassLoader来完成类的加载,同样,也采用以上三个元素来标识一个被加载了的类:类名+

    包名+ClassLoader实例ID

    2.       链接

    链接过程负责对二进制字节码的格式进行校验、初始化装载类中的静态变量以及解析类中调用的接口、类。

    完成校验后,JVM初始化类中的静态变量,并将其值赋为默认值。

    最后对类中的所有属性、方法进行验证,以确保其需要调用的属性、方法存在,以及具备应的权限(例如publicprivate域权限等),会造成NoSuchMethodErrorNoSuchFieldError等错误信息。

    3.       初始化

    初始化过程即为执行类中的静态初始化代码、构造器代码以及静态属性的初始化,在四种情况下初始化过程会被触发执行:

    调用了new

    反射调用了类中的方法;

    子类调用了初始化;

    JVM启动过程中指定的初始化类。

     

    JVM类加载顺序:

    JVM两种类装载器包括:启动类装载器和用户自定义类装载器。

    启动类装载器是JVM实现的一部分;

    用户自定义类装载器则是Java程序的一部分,必须是ClassLoader类的子类。

    JVM装载顺序:

                    Jvm启动时,由BootstrapUser-Defined方向加载类;

                    应用进行ClassLoader时,由User-DefinedBootstrap方向查找并加载类;

    1.       Bootstrap ClassLoader

    这是JVM的根ClassLoader,它是用C++实现的,JVM启动时初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOMEjre/lib/rt.jarSun JDK的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口以及实现。

    2.       Extension ClassLoader

    JVM用此classloader来加载扩展功能的一些jar包。

    3.       System ClassLoader

    JVM用此classloader来加载启动参数中指定的Classpath中的jar包以及目录,在Sun JDKClassLoader对应的类名为AppClassLoader

    4.       User-Defined ClassLoader

    User-DefinedClassLoaderJava开发人员继承ClassLoader抽象类自行实现的ClassLoader,基于自定义的ClassLoader可用于加载非Classpath中的jar以及目录。

     

    ClassLoader抽象类的几个关键方法:

    (1)       loadClass

    此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从parent ClassLoader中寻找,如仍然没找到,则从System ClassLoader中寻找,最后再调用findClass方法来寻找,如要改变类的加载顺序,则可覆盖此方法

    (2)       findLoadedClass

    此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native的方法。

    (3)       findClass

    此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。

    (4)       findSystemClass

    此方法负责从System ClassLoader中寻找类,如未找到,则继续从Bootstrap ClassLoader中寻找,如仍然为找到,则返回null

    (5)       defineClass

    此方法负责将二进制的字节码转换为Class对象

    (6)       resolveClass

    此方法负责完成Class对象的链接,如已链接过,则会直接返回。

     

    四、         JVM执行引擎

    在执行方法时JVM提供了四种指令来执行:

    1invokestatic:调用类的static方法

    2invokevirtual:调用对象实例的方法

    3invokeinterface:将属性定义为接口来进行调用

    4invokespecialJVM对于初始化对象(Java构造器的方法为:<init>)以及调用对象实例中的私有方法时。

     

    主要的执行技术有:

    解释,即时编译,自适应优化、芯片级直接执行

    1)解释属于第一代JVM

    2)即时编译JIT属于第二代JVM

    3)自适应优化(目前SunHotspotJVM采用这种技术)则吸取第一代JVM和第二代

    JVM的经验,采用两者结合的方式

    开始对所有的代码都采取解释执行的方式,并监视代码执行情况,然后对那些经常调用的方法启动一个后台线程,将其编译为本地代码,并进行优化。若方法不再频繁使用,则取消编译过的代码,仍对其进行解释执行。

     

    五、         JVM运行时数据区

    第一块:PC寄存器

    PC寄存器是用于存储每个线程下一步将执行的JVM指令,如该方法为native的,则PC寄存器中不存储任何信息。

    第二块:JVM

    JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:booleancharbyteshortintlongfloatdouble)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址

    第三块:堆(Heap

    它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。

    (1)       堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的

    (2)       Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLABThread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配

    (3)       TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。

    第四块:方法区域(Method Area

    1)在Sun JDK中这块区域对应的为PermanetGeneration,又称为持久代。

    2)方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class

    对象中的getNameisInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。

    第五块:运行时常量池(Runtime Constant Pool

    存放的为类中的固定的常量信息、方法和Field的引用信息等,其空间从方法区域中分配。

    第六块:本地方法堆栈(Native Method Stacks

    JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。

     

    六、         JVM垃圾回收

    GC的基本原理:将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停

    1)对新生代的对象的收集称为minor GC

    2)对旧生代的对象的收集称为Full GC

    3)程序中主动调用System.gc()强制执行的GCFull GC

    不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:

    1)强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)

    2)软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC

    3)弱引用:在GC时一定会被GC回收

    4)虚引用:由于虚引用只是用来得知对象是否被GC

      文章来自:http://www.open-open.com/lib/view/open1408453806147.html

    展开全文
  • Jvm 工作原理

    2019-08-22 14:44:29
    jvm 工作原理 </h1> <div class="clear"></div> <div class="postBody"> 作为一名Java使用者,掌握JVM的体系结构也是必须的。 说起Java,人们首先想到的是Java编...

    jvm 工作原理

            </h1>
            <div class="clear"></div>
            <div class="postBody">
    

    作为一名Java使用者,掌握JVM的体系结构也是必须的。 说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。它们的关系如下图所示:

    运行期环境代表着Java平台,开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件),再然后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行。

    Java平台由Java虚拟机和Java应用程序接口搭建,Java语言则是进入这个平台的通道,用Java语言编写并编译的程序可以运行在这个平台上。这个平台的结构如下图所示:

    在Java平台的结构中, 可以看出,Java虚拟机(JVM) 处在核心的位置,是程序与底层操作系统和硬件无关的关键。它的下方是移植接口,移植接口由两部分组成:适配器和Java操作系统, 其中依赖于平台的部分称为适配器;JVM 通过移植接口在具体的平台和操作系统上实现;在JVM 的上方是Java的基本类库和扩展类库以及它们的API, 利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台, 就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离,从而实现了Java 的平台无关性。

    JVM在它的生存周期中有一个明确的任务,那就是运行Java程序,因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。下面我们从JVM的体系结构和它的运行过程这两个方面来对它进行比较深入的研究。

    1、Java虚拟机的体系结构

    ·每个JVM都有两种机制:

    ①类装载子系统:装载具有适合名称的类或接口

    ②执行引擎:负责执行包含在已装载的类或接口中的指令

    ·每个JVM都包含

    方法区、Java堆、Java栈、本地方法栈、指令计数器及其他隐含寄存器

     

     

    对于JVM的学习,在我看来这么几个部分最重要:

    Java代码编译和执行的整个过程

    JVM内存管理及垃圾回收机制

    下面分别对这几部分进行说明:

    2、Java代码编译和执行的整个过程

    也正如前面所说,Java代码的编译和执行的整个过程大概是:开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件),再然后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行。

    (1)Java代码编译是由Java源码编译器来完成,也就是Java代码到JVM字节码(.class文件)的过程。 流程图如下所示:

    (2)Java字节码的执行是由JVM执行引擎来完成,流程图如下所示:

     

    Java代码编译和执行的整个过程包含了以下三个重要的机制:

    ·Java源码编译机制

    ·类加载机制

    ·类执行机制

     

    (1)Java源码编译机制

    Java 源码编译由以下三个过程组成:

    ①分析和输入到符号表

    ②注解处理

    ③语义分析和生成class文件

    流程图如下所示:

    最后生成的class文件由以下部分组成:

    ①结构信息:包括class文件格式版本号及各部分的数量与大小的信息

    ②元数据:对应于Java源码中声明与常量的信息。包含类/继承的超类/实现的接口的声明信息、域与方法声明信息和常量池

    ③方法信息:对应Java源码中语句和表达式对应的信息。包含字节码、异常处理器表、求值栈与局部变量区大小、求值栈的类型记录、调试符号信息

    (2)类加载机制 JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:

    ①Bootstrap ClassLoader

    负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

    ②Extension ClassLoader

    负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

    ③App ClassLoader

    负责记载classpath中指定的jar包及目录中class

    ④Custom ClassLoader

    属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

     

    加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

    (3)类执行机制

    JVM是基于堆栈的虚拟机。JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

    JVM执行class字节码,线程创建后,都会产生程序计数器(PC)和栈(Stack),程序计数器存放下一条要执行的指令在方法内的偏移量,栈中存放一个个栈帧,每个栈帧对应着每个方法的每次调用,而栈帧又是有局部变量区和操作数栈两部分组成,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法执行过程中产生的中间结果。栈的结构如下图所示:

    3、JVM内存管理及垃圾回收机制

    JVM内存结构分为:方法区(method),栈内存(stack),堆内存(heap),本地方法栈(java中的jni调用),结构图如下所示:

    (1)堆内存(heap)

    所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。 操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete语句才能正确的释放本内存空间。但由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。这时由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,它不是在堆,也不是在栈,而是直接在进程的地址空间中保留一块内存,虽然这种方法用起来最不方便,但是速度快,也是最灵活的。堆内存是向高地址扩展的数据结构,是不连续的内存区域。由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大

    (2)栈内存(stack)

    在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 由系统自动分配,速度较快。但程序员是无法控制的。

    堆内存与栈内存需要说明:

    基础数据类型直接在栈空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量 。方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量new出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。方法调用时传入的literal参数,先在栈空间分配,在方法调用完成后从栈空间收回。字符串常量、static在DATA区域分配,this在堆空间分配。数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小。

    如:

    (3)本地方法栈(java中的jni调用)

    用于支持native方法的执行,存储了每个native方法调用的状态。对于本地方法接口,实现JVM并不要求一定要有它的支持,甚至可以完全没有。Sun公司实现Java本地接口(JNI)是出于可移植性的考虑,当然我们也可以设计出其它的本地接口来代替Sun公司的JNI。但是这些设计与实现是比较复杂的事情,需要确保垃圾回收器不会将那些正在被本地方法调用的对象释放掉。

    (4)方法区(method)

    它保存方法代码(编译后的java代码)和符号表。存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(Permanet Generation)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值。

    垃圾回收机制

    堆里聚集了所有由应用程序创建的对象,JVM也有对应的指令比如 new, newarray, anewarray和multianewarray,然并没有向 C++ 的 delete,free 等释放空间的指令,Java的所有释放都由 GC 来做,GC除了做回收内存之外,另外一个重要的工作就是内存的压缩,这个在其他的语言中也有类似的实现,相比 C++ 不仅好用,而且增加了安全性,当然她也有弊端,比如性能这个大问题。

     

    4、Java虚拟机的运行过程示例

    上面对虚拟机的各个部分进行了比较详细的说明,下面通过一个具体的例子来分析它的运行过程。

    虚拟机通过调用某个指定类的方法main启动,传递给main一个字符串数组参数,使指定的类被装载,同时链接该类所使用的其它的类型,并且初始化它们。例如对于程序:

    编译后在命令行模式下键入: java HelloApp run virtual machine

    将通过调用HelloApp的方法main来启动java虚拟机,传递给main一个包含三个字符串"run"、"virtual"、"machine"的数组。现在我们略述虚拟机在执行HelloApp时可能采取的步骤。

    开始试图执行类HelloApp的main方法,发现该类并没有被装载,也就是说虚拟机当前不包含该类的二进制代表,于是虚拟机使用ClassLoader试图寻找这样的二进制代表。如果这个进程失败,则抛出一个异常。类被装载后同时在main方法被调用之前,必须对类HelloApp与其它类型进行链接然后初始化。链接包含三个阶段:检验,准备和解析。检验检查被装载的主类的符号和语义,准备则创建类或接口的静态域以及把这些域初始化为标准的默认值,解析负责检查主类对其它类或接口的符号引用,在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过程如下:

    本文来自:http://blog.csdn.net/CSDN_980979768/article/details/47281037?locationNum=7&fps=1

    <div id="blog_post_info">
    
    0
    0
    <div class="clear"></div>
    <div id="post_next_prev">
    
    <a href="https://www.cnblogs.com/yueminghai/p/6635928.html" class="p_n_p_prefix">« </a> 上一篇:    <a href="https://www.cnblogs.com/yueminghai/p/6635928.html" title="发布于 2017-03-28 17:29">nginx实现MySQL负载均衡</a>
    <br>
    <a href="https://www.cnblogs.com/yueminghai/p/6639367.html" class="p_n_p_prefix">» </a> 下一篇:    <a href="https://www.cnblogs.com/yueminghai/p/6639367.html" title="发布于 2017-03-29 13:40">监控nginx服务</a>
    
    posted @ 2017-03-29 12:27 逆疯~ 阅读(5403) 评论(0) 编辑 收藏
    </div><!--end: topics 文章、评论容器-->
    
    展开全文
  • JVM工作原理

    2017-08-10 17:46:00
    参考来源:...参考来源:http://www.cnblogs.com/lishun1005/p/6019678.html (转)Java 详解 JVM 工作原理和流程 参考来源:http://blog.csdn.net/stanlee_...

    参考来源:http://blog.csdn.net/witsmakemen/article/details/28600127/          java中JVM的原理

    参考来源:http://www.cnblogs.com/lishun1005/p/6019678.html   (转)Java 详解 JVM 工作原理和流程

    参考来源:http://blog.csdn.net/stanlee_0/article/details/51171382     什么是jvm?

    转载于:https://www.cnblogs.com/1020182600HENG/p/7340557.html

    展开全文
  • JVM 工作原理

    2013-06-22 21:37:58
    JVM的生命周期 JVM的体系结构 JVM类加载器 JVM执行引擎 JVM运行时数据区 JVM垃圾回收
  • Jvm工作原理

    千次阅读 2018-10-09 17:03:31
    原理:当一条线程需要请求一把已经被占用的锁时,并不会进入阻塞状态,而是继续持有CPU执行权等待一段时间,该过程称为『自旋』。 优点:由于自旋等待锁的过程线程并不会引起上下文切换,因此比较高效; 缺点...

    1.哪些内存需要回收

    2.什么时候回收

    3.如何回收

     

    内存动态分配

    内存的回收技术

     

    目的:

    排查内存溢出

    内存泄漏问题

     

    主要讨论的是堆区

    pc\虚拟机栈、本地方法栈 和线程相关

    栈帧跟随方法  具有确认性

     

    1.判断哪些对象还活着

    引用计数法

    优点:容易实现

    缺点:循环依赖

    A引用B  B引用A

    可达性分析

    通过引用链 

    当一个对象没有任何引用链gc不可达 证明不可用

     

    在JAVA中可做为GC roots对象包括

    虚拟机栈中引用的对象(栈帧中的本地变量表)

    方法区类静态属性引用的对象

    方法区中常量引用的对象

    jni引用的对象

     

     

    引用:

    refrence类型存储的类型是另外一块的地址 就认为该块儿是一个引用

     

    扩展

    强引用  强引用存在永远不会回收

    软引用  有用非必须内存溢出之前会回收

    弱引用  非必须 只能存在到下次gc之前

    虚引用 无法通过虚引用获得实例  在这个对象回收时收到一个系统通知

    2.清理时间

    两次标记

    第一次标记  赛选 1有没有必要执行finalize  条件 gc 调过 或者没有覆盖

     

     

    有必要   放在Fqueue  稍后有一个Finalizer线程执行   并不能保证执行结束  原因??  最后一次机会

    第一次finalize有机会自救

    第二次标记

     

    而一个对象的finliza只会被就一次

     

    finalize不推荐用来自救

     

    方法区  在方法区垃圾回收性价比低

     

    新生代一次回收70-90

     

    方法区  废弃的常量  无用的类

     

    废弃常量   是否有对象引用它

    无用类  1.该类的实例都已经被回收 2.加载的classloader已经被回收 3.没有任何地方被引用

     

    虚拟机提供参数控制

    在ByteCode框架有用

     

    3.垃圾收集算法

    Mark-Sweep

    特点:效率低  内存碎片

    复制算法   

    分块 标记 移动  

    适用于存活率较低  占用内存较小的

    商业 分块 Eden  survior

     

    每次使用 90 

    生命时间长的 分配担保

     

    标记整理 

    伴随着移动

     

    分代收集

     

    HotSpot

    枚举根节点************

    安全点************

    安全区域****************

     

    垃圾收集器

    是具体实现

    HotSpot的提供

    CMS  目的:获取最短回收停顿时间  基于标记清除

    缺点:

    1.对cpu资源敏感

    2.无法浮动垃圾

    3.内存碎片

     

    G1 并行并发  分带收集 空间整合 可预测停顿

    原理把内存化整为零

     

    内存分配和回收策略

    大对象直接进OLd区域

    长期存活进old区  age计数器

    MinorGC  新生代gc

    各种垃圾收集器之间的比较

    https://blog.csdn.net/tjiyu/article/details/53983650

     

     

    虚拟机类的加载机制

    双亲委派加载

     

    MajorGC  FullGC  比较慢

     

     

    Tomcat的类的加载机制

    OSGI

    字节码生成技术和动态带来

    Retrotranslator

    自己动手实现远程执行功能

     

     

    优化没看

     

     

    Java语言中的线程安全

    代码本身封装了所有安全保障性的手段

    调用者无需关心多线程

     

    级别

    不可变

    调用者无需采用任何线程安全保护措施

    final  没有this引用逃逸

    比如String   它的subString replace contant 都是一个新构建的对象

    Integer也是 状态不变

    枚举类型

     

    Numbei子类是

     

    原子类不是

     

     

     

    绝对线程安全

     

    Vector并不是一个绝对安全的

     

    相对的线程安全

    要保证线程的操作是线程安全的  在调用时无需保障措施

    线程兼容

    对象本身不安全调用段正确的使用同步手段

    线程对立

    调用段是否采用同步措施都无法保证安全

     

     

    如何保证线程安全

    1.互斥同步

    悲观的并发策略

    无论是否竞争都加锁

     

    互斥是同步的一种手段   临界区 互斥量  信号量

    sychronized编译之后前后形成monitorenter o

     

    执行monitor指令要先尝试获取对象的锁  

    如果获取失败要进行阻塞等待直到对象锁被另外一个线程释放

    sychronized特点1可重入

    原生语法的互斥锁

     

    阻塞或者唤醒操作系统帮忙完成  线程状态切换  重量级的操作、

    虚拟机自身的优化 自旋锁 

     

    JUC ReentrantLock

     

    API层面的互斥锁 lock/unlock配合try/finally语句来完成

     

    高级特性

    1.等待可中断  正在等待可放弃等待做其他事

     

    2.可实现公平锁

    按照申请锁的顺序依次获取锁

    3.锁可以绑定多个条件

     

     

    jdk1.6有了很大的优化

    虚拟机优化了狠多不需要的加锁

    状态切换

    维护锁计数器

    是否需要唤醒

     

    非阻塞同步

    基于冲突检测的乐观并发锁

    如果发生冲突  则补偿措施  cas   大多数不会把线程挂起

    需要依赖硬件  tat fai sweep cas(cmpxchg casa ) ll/sc

     

    CAS指令 3个操作数  v a b  

    该操作由Unsafe里的compareAndSwap 几个方法包装提供  虚拟机内部特殊处理

     

    Unsafe只有启动内加载器才能访问 只能通过JUC原子类 或者反射调用

     

    有一个漏洞  原子引用类 AtomicStampedReference   它可以通过控制变量值的版本保证CAS正确性  鸡肋

     

    无同步方案

    如果不存在共享数据  

    可重入代码   在代码执行时被打断

    不依赖堆上的数据和公共资源 状态量参数传入 不调用非可重入方法

     

    返回值可预测

    线程本地存储 可见范围在一个线程内

     

    应用:大部分的生产者消费者架构

    web交互模型中一个请求对应一个服务器线程 线程本地存储

    ThreadLocal 可以实现线程的本地存储

     

    锁的优化

    适应性自旋

    锁消除

    锁粗化

    轻量级锁

    无竞争的情况下使用cas

    传统的互斥量

    在没有竞争下减少互斥量

     

    偏向锁

     

    自旋锁

    • 背景:互斥同步对性能最大的影响是阻塞,挂起和恢复线程都需要转入内核态中完成;并且通常情况下,共享数据的锁定状态只持续很短的一段时间,为了这很短的一段时间进行上下文切换并不值得。
    • 原理:当一条线程需要请求一把已经被占用的锁时,并不会进入阻塞状态,而是继续持有CPU执行权等待一段时间,该过程称为『自旋』。
    • 优点:由于自旋等待锁的过程线程并不会引起上下文切换,因此比较高效;
    • 缺点:自旋等待过程线程一直占用CPU执行权但不处理任何任务,因此若该过程过长,那就会造成CPU资源的浪费。
    • 自适应自旋:自适应自旋可以根据以往自旋等待时间的经验,计算出一个较为合理的本次自旋等待时间。

     

    锁清除

    编译器会清除一些使用了同步,但同步块中没有涉及共享数据的锁,从而减少多余的同步。

    锁粗化

    若有一系列操作,反复地对同一把锁进行上锁和解锁操作,编译器会扩大这部分代码的同步块的边界,从而只使用一次上锁和解锁操作。

    轻量级锁

    • 本质:使用CAS取代互斥同步。
    • 背景:『轻量级锁』是相对于『重量级锁』而言的,而重量级锁就是传统的锁。
    • 轻量级锁与重量级锁的比较:
      • 重量级锁是一种悲观锁,它认为总是有多条线程要竞争锁,所以它每次处理共享数据时,不管当前系统中是否真的有线程在竞争锁,它都会使用互斥同步来保证线程的安全;
      • 而轻量级锁是一种乐观锁,它认为锁存在竞争的概率比较小,所以它不使用互斥同步,而是使用CAS操作来获得锁,这样能减少互斥同步所使用的『互斥量』带来的性能开销。
    • 实现原理:
      • 对象头称为『Mark Word』,虚拟机为了节约对象的存储空间,对象处于不同的状态下,Mark Word中存储的信息也所有不同。
      • Mark Word中有个标志位用来表示当前对象所处的状态。
      • 当线程请求锁时,若该锁对象的Mark Word中标志位为01(未锁定状态),则在该线程的栈帧中创建一块名为『锁记录』的空间,然后将锁对象的Mark Word拷贝至该空间;最后通过CAS操作将锁对象的Mark Word指向该锁记录;
      • 若CAS操作成功,则轻量级锁的上锁过程成功;
      • 若CAS操作失败,再判断当前线程是否已经持有了该轻量级锁;若已经持有,则直接进入同步块;若尚未持有,则表示该锁已经被其他线程占用,此时轻量级锁就要膨胀成重量级锁。
    • 前提:轻量级锁比重量级锁性能更高的前提是,在轻量级锁被占用的整个同步周期内,不存在其他线程的竞争。若在该过程中一旦有其他线程竞争,那么就会膨胀成重量级锁,从而除了使用互斥量以外,还额外发生了CAS操作,因此更慢!

    偏向锁

    • 作用:偏向锁是为了消除无竞争情况下的同步原语,进一步提升程序性能。
    • 与轻量级锁的区别:轻量级锁是在无竞争的情况下使用CAS操作来代替互斥量的使用,从而实现同步;而偏向锁是在无竞争的情况下完全取消同步。
    • 与轻量级锁的相同点:它们都是乐观锁,都认为同步期间不会有其他线程竞争锁。
    • 原理:当线程请求到锁对象后,将锁对象的状态标志位改为01,即偏向模式。然后使用CAS操作将线程的ID记录在锁对象的Mark Word中。以后该线程可以直接进入同步块,连CAS操作都不需要。但是,一旦有第二条线程需要竞争锁,那么偏向模式立即结束,进入轻量级锁的状态。
    • 优点:偏向锁可以提高有同步但没有竞争的程序性能。但是如果锁对象时常被多条线程竞争,那偏向锁就是多余的。
    • 偏向锁可以通过虚拟机的参数来控制它是否开启。

    锁的膨胀

    消除无竞争下同步原语

    偏向于第一个获得它的线程 该锁没有被其他线程获取 则 持有偏向锁的线程永不加锁

     

     

     

     

     

     

     

     

     

     

    展开全文
  • jvm 工作原理

    2017-09-30 20:48:54
    作为一名Java使用者,掌握JVM的体系结构也是必须的。 说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,327
精华内容 1,330
关键字:

jvm工作原理