JVM

作者: tdwmarlboro | 来源:发表于2018-05-25 14:53 被阅读0次

    JVM内存结构:

    方法区:用户存储已被虚拟机加载的类信息常量,静态常量,即时编译器编译后的代码等数据。异常状态OutOfMemoryError 永久代Hotspot虚拟机特有的概念,是方法区的一种实现

    堆:是JVM所管理的内存中最大的一块。唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”。异常状态OutOfMemoryError

    虚拟机栈:描述的是java方法执行的内存模型,每个方法在执行时都会创建一个栈帧,用户存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。异常状态OutOfMemoryErrorStackOverflowError

    本地方法栈:与虚拟机栈所发挥的作用相似。它们之间的区别不过是虚拟机栈为虚拟机执行java方法,而本地方法栈为虚拟机使用到的Native方法服务。

    程序计数器:一块较小的内存,当前线程所执行的字节码的行号指示器。字节码解释器工作时,就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

    Java内存模型:

    线程A和线程B之间若要通信的话, 必须经历下面两个步骤 (1)线程A和线程A本地内存中更新过的共享变量刷新到主存中去。 (2)线程B到主存中去读取线程A之前更新过的共享变量。

    虚拟机规范视图通过JMM来屏蔽掉各种硬件和操作系统的内存访问差异,主要目标是定义程序中各个变量的访问限制,即在虚拟机将变量存储到内存和从内存中取出变量这样的底层细节;

    主内存与工作内存:Java内存模型规定了所有的变量都存储在主内存中,每个线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中操作,而不能直接读写主内存中的变量。

    可见性(主内存和工作内存)、原子性(volatile的long是具备原子性的)、有序性(happen-before规则)

    JVM加载过程:

    加载、验证、准备、解析、初始化、使用和卸载

    类的加载器:

    BootStrap-》jre/lib/rt.jar

    ExtClassLoader-》jre/lib/ext/*.jar

    AppClassLoader-》ClassPath指定下所有jar或目录

    类的连接:

    验证、准备、解析

    类的初始化时机:

    1、创建对象的实例:我们new对象的时候,会引发类的初始化,前提是这个类没有被初始化

    2、调用类的静态属性或者为静态属性赋值

    3、调用类的静态方法

    4、通过class文件反射创建对象

    5、初始化一个类的子类:使用子类的时候先初始化父类

    6、java虚拟机启动时被标记为启动类的类:就是我们的main方法所在的类

    通过ClassLoader.loadClass 方法可以手动加载一个Java 类到虚拟机中,并返回Class类型的引用。

    实现:双亲委托机制

    1、loadClass方法实现了双亲委派的类加载机制,如果需要自定义类加载器,建议重写内部的findClass方法,而非loadClass方法

    Launcher$ExtClassLoader的实现是遵循的双亲委派模型,它重写的是findClass方法

    Launcher$AppClassLoader的实现是没有遵循双亲委派模型的,它重的是loadClass方法

    重写findClass方法是符合双亲委派模式的,它保证了相同全限定名的类是不会被重复加载到JVM中

    2、通过debug发现loadClass方法最终会执行native方法defineClass1进行类的加载,即读取对应class文件的二进制数据到虚拟机中进行解析

    Java内存模型:

    内存屏障(memory barriers)

    又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:

    1、保证特定操作的执行顺序

    2、影响某些数据(或者是某条指令的执行结果)的内存可见性

    Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

    Memory Barrier 所做的另外一件事是强制刷出各种CPU cache,如一个Write-Barrier(写入屏障)将刷出所有在Barrier 之前写入cache 的数据,因此,任何CPU上的线程都能读取到这些数据的最新版本。

    volatile 是基于MemoryBarrier实现的。

    如果一个变量是volatile修饰的,JMM会在写入这个字段之后插进一个Write-Barrier 指令,并在读这个字段之前插入一个Read-Barrier指令。

    这意味着,如果写入一个volatile变量a,可以保证:

    1、一个线程写入变量a后,任何线程访问该变量都会拿到最新值。

    2、在写入变量a之前的写入操作,其更新的数据对于其他线程也是可见的。因为Memory Barrier会刷出cache中的所有先前的写入。

    Happens-before

    在JMM中,如果一个操作的执行结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before 关系,这个的两个操作既可以在同一个线程,也可以在不同的两个线程中。

    同步器AQS(AbstractQueuedSynchronizer)

    在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReadWriteLock(实现类ReentrantReadWriteLock),内部实现都依赖AbstractQueuedSynchronizer类。

    内存模型定义了一个充分必要条件,保证其它CPU的写入动作对该CPU是可见的,而且该CPU的写入动作对其它CPU也是可见的。

    JVN性能调优以及常用的JDK的命令行工具:

    JVM调优:CPU使用率与Load值偏大(Thread count 以及GC count)、关键接口响应时间很慢(GC time以及GC log中的STW的时间)、发生Full GC或者OldCMS GS非常频繁(内存泄漏);

    JVM停顿(尽量避免Full GC、关闭偏向锁、输出GC日志到内存文件系统、关闭JVm输出的jstat日志);

    将Java性能优化分为4个层级:应用层、数据库层、框架层、JVM层。每层优化难度逐级增加,涉及的知识和解决的问题也会不同。比如应用层需要理解代码逻辑,通过Java线程栈定位有问题代码行等;数据库层面需要分析SQL、定位死锁等;框架层需要懂源代码,理解框架机制;JVM层需要对GC的类型和工作机制有深入了解,对各种JVM参数作用了然于胸

    OS的诊断主要关注的是CPu、Memory、I/O三个方面。top、vmstat、free-m、uistat;常用的java应用诊断包括线程、堆栈、GC等方面的诊断,可以使用jstatck、jstat、jamp

    JVM锁有4种状态:无锁、偏向锁、轻量级锁、重量级锁

    相关文章

      网友评论

        本文标题:JVM

        本文链接:https://www.haomeiwen.com/subject/irektftx.html