一、Java内存模型
共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另外一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每一个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其余的硬件和编译器优化。
按照官方的说法:Java 虚拟机具备一个堆,堆是运行时数据区域,全部类实例和数组的内存均今后处分配。
从上图来看,线程A与线程B之间如要通讯的话,必需要经历下面2个步骤:
1)首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
2)而后,线程B到主内存中去读取线程A以前已更新过的共享变量。
下面经过示意图来讲明这两个步骤:
如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在本身的本地内存A中。当线程A和线程B须要通讯时,线程A首先会把本身本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。
从总体来看,这两个步骤实质上是线程A在向线程B发送消息,并且这个通讯过程必需要通过主内存。JMM经过控制主内存与每一个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。
总结:什么是Java内存模型:java内存模型简称jmm,定义了一个线程对另外一个线程可见。共享变量存放在主内存中,每一个线程都有本身的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,因此就会发生线程安全问题。
二、Java内存模型包含的内容
Java内存模型定义了一种多线程访问Java内存的规范。java内存模型的几部份内容:
1)Java内存模型将内存分为了 主内存和工做内存 。类的状态,也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在本身的工做内存中有一份拷贝,运行本身线程代码的时候,用到这些变量,操做的都是本身工做内存中的那一份。在线程代码执行完毕以后,会将最新的值更新到主内存中去。
2)定义了几个原子操做,用于操做主内存和工做内存中的变量。
3)定义了volatile变量的使用规则。
4)happens-before,即先行发生原则,定义了操做A必然先行发生于操做B的一些规则,好比在同一个线程内控制流前面的代码必定先行发生于控制流后面的代码、一个释放锁unlock的动做必定先行发生于后面对于同一个锁进行锁定lock的动做等等,只要符合这些规则,则不须要额外作同步措施,若是某段代码不符合全部的happens-before规则,则这段代码必定是线程非安全的。
三、JVM内存管理
JVM主要管理两种内存:堆和非堆
1)堆内存(Heap Memory)是在 Java 虚拟机启动时建立
>堆是Java代码可及的内存,留给开发人员使用的
>存放java对象
2)非堆内存(Non-heap Memory)是在JVM堆以外的内存。
>非堆是JVM留给本身用的,包含方法区、JVM内部处理或优化所需的内存(如 JITCompiler,Just-in-time Compiler,即时编译后的代码缓存)、每一个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码。
>存放类加载信息和其它meta-data
3)其余内存
>存放JVM 自身代码等
在JVM启动时,就已经保留了固定的内存空间给Heap内存,这部份内存并不必定都会被JVM使用,可是能够肯定的是这部分保留的内存不会被其余进程使用,这部份内存大小由-Xmx 参数指定。而另外一部份内存在JVM启动时就分配给JVM,做为JVM的初始Heap内存使用,这部份内存是由 -Xms 参数指定。
四、CAS 理解
CAS,全称为Compare and Set,即比较-设置。假设有三个操做数: 内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,才会将内存值修改成B并返回true,不然什么都不作并返回false 。固然CAS必定要volatile变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,不然旧的预期值A对某条线程来讲,永远是一个不会变的值A,只要某次CAS操做失败,永远都不可能成功。
五、JRE与JVM、JDK的区别:
1)JVM就是咱们常说的java虚拟机,它是整个java实现跨平台的 最核心的部分,全部的java程序会首先被编译为.class的类文件,这种类文件能够在虚拟机上执行,也就是说class并不直接与机器的操做系统相对应,而是通过虚拟机间接与操做系统交互,由虚拟机将程序解释给本地系统执行。JVM 的主要工做是解释本身的指令集(即字节码)到 CPU 的指令集或 OS 的系统调用,保护用户免被恶意程序骚扰。JVM 对上层的 Java 源文件是不关心的,它关注的只是由源文件生成的类文件( class file )。类文件的 组成包括 JVM 指令集,符号表以及一些补助信息。
2)JRE java runtime environment
JRE是指java运行环境。光有JVM还不能成class的 执行,由于在解释class的时候JVM须要调用解释所须要的类库lib。 在JDK的安装目 录里你能够找到jre目录,里面有两个文件夹bin和lib,在 这里能够认为bin里的就是jvm,lib中则是jvm工做所须要的类库,而jvm和 lib和起来就称为jre。因此,在你写完java程序编译成.class以后,你能够把这个.class文件 和jre一块儿打包发给朋友,这样你的朋友就 能够运行你写程序了。(jre里有运行.class的java.exe)JRE里面有一个 JVM , JRE 与具体的 CPU 结构和操做系统有关,咱们从 Sun 下载 JRE 的时候就看到了不一样的各类版本,同 JVM 一块儿组成 JRE 的还有 一些 API (如 awt , swing 等), JRE 是 运行 Java 程序必不可少的.
3)JDK -- java development kit
JDK是java开发工具包,基本上每一个学java的人都会先在机器 上装一个JDK,在JDK的安装目录下面 六个文件夹、一个src类库源码压缩包、和其余几个声明文件。其中,真正在运行java时起做用的 是如下四个文件夹:bin、include、lib、 jre。如今咱们能够看出这样一个关系,JDK包含JRE,而JRE包 含JVM。
bin:最主要的是编译器(javac.exe)
include:java和JVM交互用的头文件
lib:类库
jre:java运行环境
(注意:这里的bin、lib文件夹和jre里的bin、lib是 不一样的)总的来讲JDK是用于java程序的开发,而jre则是只能运行class而没有编译的功能。eclipse、idea等 其余IDE有本身的编译器而不是用JDK bin目录中自带的,因此在安装时你会发现他们只要求你 选中jre路径就ok了
三者之间的关系:Java 程序的字节码文件能够放到任意装有 JRE 的计算机运行,再由不一样 JRE 的将它们转化成相应的机器代码,这就实现了 Java 程序的可移植性。
六、JVM类加载原理
JVM在运行时会产生三个ClassLoader:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.
1)Bootstrap是用C++编写的,咱们在Java中看不到它,是null,它用来加载核心类库。
关于Bootstrap ClassLoader,在JVM源代码中这样写道:
static const char classpathFormat[] =
"%/lib/rt.jar: "
"%/lib/i18n.jar: "
"%/lib/sunrsasign.jar: "
"%/lib/jsse.jar: "
"%/lib/jce.jar: "
"%/lib/charsets.jar: "
"%/classes ";
为何不须要在classpath中加载这些类了?是由于在JVM启动的时候就自动加载了,而且在运行过程当中根本不能修改Bootstrap加载路径。
2)Extension ClassLoader用来加载扩展类,即/lib/ext中的类
3)AppClassLoader才是加载Classpath的
ClassLoader加载类用的是委托模型。即先让Parent类(而不是Super,不是继承关系)寻找,Parent找不到才本身找。看来ClassLoader仍是蛮孝顺的。三者的关系为:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。加载一个类时,首先BootStrap先进行寻找,找不到再由ExtClassLoader寻找,最后才是AppClassLoader。
为何要设计的这么复杂呢?其中一个重要缘由就是安全性。好比在Applet中,若是编写了一个java.lang.String类并具备破坏性。假如不采用这种委托机制,就会将这个具备破坏性的String加载到了用户机器上,致使破坏用户安全。但采用这种委托机制则不会出现这种状况。由于要加载java.lang.String类时,系统最终会由Bootstrap进行加载,这个具备破坏性的String永远没有机会加载。
java