JVM原理与深度调优(一)

什么是jvm

jvm是java虚拟机 运行在用户态、经过应用程序实现java代码跨平台、与平台无关、其实是"一次编译,处处执行"java

1.从微观来讲编译出来的是字节码!去到哪一个平台都能用,只要有那个平台的JDK就能够运行!字码比如是一我的,平台比如为国家,JDK比如这个国家的语言!只要这我的(字节码)有了这个国家的语言(JDK)就能够在这个国家(平台)生活下去。
2.JDK 是整个Java的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具和Java基础的类库(rt.jar)。
3.Java虚拟机(JVM)一种用于计算机设备的规范,可用不一样的方式(软件或硬件)加以实现。编译虚拟机的指令集与编译微处理器的指令集很是相似。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。
4.java编译出来的是一种“java字节码”,由虚拟机去解释执行。 而c和c++则编译成了二进制,直接交由操做系统执行。
5.所谓的一次编译、处处执行,即只需在一个地方编译,在其余各个平台下均可以执行。
6.与平台无关指的是JAVA只运行在本身的JVM上,不须要依赖任何其余的底层类,因此和操做系统没有任何联系,平台是说运行的系统linux

内存结构图

 

 class文件 

class文件径打破了C或者C++等语言所遵循的传统,使用这些传统语言写的程序一般首先被编译,而后被链接成单独的、专门支持特定硬件平台和操做系统的二进制文件。一般状况下,一个平台上的二进制可执行文件不能在其余平台上工做。而Java class文件是能够运行在任何支持Java虚拟机的硬件平台和操做系统上的二进制文件。c++

 

执行过程

 执行过程简介

当编译和链接一个C++程序时,所得到的可执行 二进制文件只能在指定的硬件平台和操做系统上运行,由于这个二进制文件包含了对目标处理器的 机器语言。而Java 编译器把Java源文件的指令翻译成 字节码,这种字节码就是Java 虚拟机的“机器语言”。
与普通程序不一样的是,Java程序(class文件)并非本地的可执行程序。当运行Java程序时,首先运行 JVM(Java虚拟机),而后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫作Class Loader。

JVM中的ClassLoader

JVM自己包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM同样,BootstrapClassLoader是用 本地代码实现的,它负责加载核心JavaClass(即全部java.*开头的类)。
另外JVM还会提供两个ClassLoader,它们都是用 Java语言编写的,由BootstrapClassLoader加载;其中Extension ClassLoader负责加载扩展的Javaclass(例如全部javax.*开头的类和存放在JRE的ext目录下的类)ApplicationClassLoader负责加载应用程序自身的类。
当运行一个程序的时候,JVM启动,运行bootstrapclassloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),而后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
 

第一个Class文件、经过javac编译成字节码、字节码以后有个ClassLoader叫类加载器,由于java.class文件到JVM内部运行起来须要有个装载过程、从物理的文件到内存的结构、好比加载、链接、初始化。程序员

linux应用程序有个进程地址空间,对进程地址空间的解释:bootstrap

linux采用虚拟内存管理技术,每个进程都有一个3G大小的独立的进程地址空间,这个地址空间就是用户空间。每一个进程的用户空间都是彻底独立、互不相干的。进程访问内核空间的方式:系统调用和中断。
    建立进程等进程相关操做都须要分配内存给进程。这时进程申请和得到的不是物理地址,仅仅是虚拟地址。 
实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际页框的程序。该异常是虚拟内存机制赖以存在的基本保证,它会告诉内核去为进程分配物理页,并创建对应的页表,这以后虚拟地址才实实在在的映射到了物理地址上。安全

Linux操做系统采用虚拟内存技术,全部进程之间以虚拟方式共享内存。进程地址空间由每一个进程中的线性地址区组成,并且更为重要的特色是内核容许进程使用该空间中的地址。一般状况况下,每一个进程都有惟一的地址空间,并且进程地址空间之间彼此互不相干。可是进程之间也能够选择共享地址空间,这样的进程就叫作线程。网络

基本上全部linux应用程序都会遵循这个规泛、有栈、有堆、对于JVM来讲、也是遵循这个规则、只不过在这个规则上作了一些改进并发

经过类加载器把Class文件装载进内存空间、装进来之后只是你的字节码,而后你须要去运行、怎么去运行呢 ?图中类加载器子系统下面都是运行区
内存空间里有:
1.方法区:被装载的class的信息存储在Methodarea的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,而后读入这个class文件内容并把它传输到虚拟机中。
2.Heap(堆):一个Java虚拟实例中只存在一个堆空间。
3.JavaStack(java的栈):虚拟机只会直接对栈执行两种操做:以帧为单位的压栈或出栈,java栈有个核心的数据、先进后出
4.Nativemethodstack(本地方法栈):经过字面意思、基本是调用系统本地的一些方法、通常在底层封装好了、直接调用
5.地址、在这里边是一个指针的概念、好比从变量到对象怎么作引用、就是地址
6.计数器:主要作字节码解析的时候要记住它的位置、能够理解为一个标记
7.执行引擎:数据、字节码作一些业务处理、最终达到想要的结果
8.本地方法接口:基本是底层系统、好比IO网络、调用操做系统自己
9.本地方法库:为了兼容、实现跨平台有不一样的库 、兼容平台性
额外数据信息指的是本地方法接口和本地方法库jvm

 

JMM

java的内存模型函数


你们可能听过一个词、叫线程安全、在写高并发的时候就会有线程安全问题、java里边为何会出现线程安全问题呢、由于有JMM的存在、它会把内存分为两个区域(一个主内存、一个是工做内存)工做内存是每一个java栈所私有的
由于要运行速度快、须要把主内存的数据放到本地内存中、而后进行计算、计算完之后再把数据回显回去

 

JMM有两个区域、主内存和栈内存、
java线程可能不止一个、可能有多个栈、如今须要三个线程同时作个运算、主内存初始值x=0 须要把x=0都要装载在本身的内存里边去、至关于有一个
副本、如今初始值和三个栈都是x=0
如今须要作运算
x=x+1
x=x-1
x=0
咱们的指望值是x=0,若是是单个线程跑没问题 、取回x=0、运算x=+一、回显进来主内存就是1 、栈1是1,运算x=-一、回显进来主内存就是0、栈1是0

若是多个线程同时执行、结果是不可预期的、正由于有这种结构的存在、当执行x=+一、栈1是x=1  、栈2来不及执行、栈1就已经把x=1写到主内存了 、栈2跟栈3拿过去以后初始值就不是0、可能就是1了 、这样程序就写乱了 

因此在java中就出现了不少锁、来确保线程安全 

 

 运行时数据区

PC寄存器----线程私有

PC寄存器也叫程序计数器(Program Counter Register)是一块较小的内存空间,它的做用能够看作是当前线程所执行的字节码的信号指示器。
每一条JVM线程都有本身的PC寄存器
在任意时刻,一条JVM线程只会执行一个方法的代码。该方法称为该线程的当前方法(Current Method)
若是该方法是java方法,那PC寄存器保存JVM正在执行的字节码指令的地址
若是该方法是native,那PC寄存器的值是undefined。
此内存区域是惟一一个在Java虚拟机规范中没有规定任何OutOfMemoryError状况的区域。

Java虚拟机栈 ----线程私有

与PC寄存器同样,java虚拟机栈(Java Virtual Machine Stack)也是线程私有的。每个JVM线程都有本身的java虚拟机栈,这个栈与线程同时建立,它的生命周期与线程相同。
虚拟机栈描述的是Java方法执行的内存模型:每一个方法被执行的时候都会同时建立一个栈帧(Stack Frame)用于存储局部变量表、操做数栈、动态连接、方法出口等信息。每个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
JVM stack 能够被实现成固定大小,也能够根据计算动态扩展。
若是采用固定大小的JVM stack设计,那么每一条线程的JVM Stack容量应该在线程建立时独立地选定。JVM实现应该提供调节JVM Stack初始容量的手段。
若是采用动态扩展和收缩的JVM Stack方式,应该提供调节最大、最小容量的手段。
JVM Stack 异常状况:
StackOverflowError:当线程请求分配的栈容量超过JVM容许的最大容量时抛出
OutOfMemoryError:若是JVM Stack能够动态扩展,可是在尝试扩展时没法申请到足够的内存去完成扩展,或者在创建新的线程时没有足够的内存去建立对应的虚拟机栈时抛出。

 本地方法栈----线程私有

Java虚拟机可能会使用到传统的栈来支持native方法(使用Java语言之外的其它语言编写的方法)的执行,这个栈就是本地方法栈(Native Method Stack)

若是JVM不支持native方法,也不依赖与传统方法栈的话,能够无需支持本地方法栈。

若是支持本地方法栈,则这个栈通常会在线程建立的时候按线程分配。

异常状况:

StackOverflowError:若是线程请求分配的栈容量超过本地方法栈容许的最大容量时抛出

OutOfMemoryError:若是本地方法栈能够动态扩展,而且扩展的动做已经尝试过,可是目前没法申请到足够的内存去完成扩展,或者在创建新的线程时没有足够的内存去建立对应的本地方法栈,那Java虚拟机将会抛出一个OutOfMemoryError异常。

Jave堆----线程公用

平时所说的java调优就是它
在JVM中,堆(heap)是可供各条线程共享的运行时内存区域,也是供全部类实例和数据对象分配内存的区域。
Java堆载虚拟机启动的时候就被建立,堆中储存了各类对象,这些对象被自动管理内存系统(Automatic Storage Management System,也便是常说的“Garbage Collector(垃圾回收器)”)所管理。这些对象无需、也没法显示地被销毁。
Java堆的容量能够是固定大小,也能够随着需求动态扩展,并在不须要过多空间时自动收缩。
Java堆所使用的内存不须要保证是物理连续的,只要逻辑上是连续的便可。
JVM实现应当提供给程序员调节Java 堆初始容量的手段,对于可动态扩展和收缩的堆来讲,则应当提供调节其最大和最小容量的手段。
Java 堆异常:
OutOfMemoryError:若是实际所需的堆超过了自动内存管理系统能提供的最大容量时抛出。

方法区----线程公用

方法区是可供各条线程共享的运行时内存区域。存储了每个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法

方法区在虚拟机启动的时候建立。

方法区的容量能够是固定大小的,也能够随着程序执行的需求动态扩展,并在不须要过多空间时自动收缩。

方法区在实际内存空间中能够是不连续的。

Java虚拟机实现应当提供给程序员或者最终用户调节方法区初始容量的手段,对于能够动态扩展和收缩方法区来讲,则应当提供调节其最大、最小容量的手段。

Java 方法区异常:

OutOfMemoryError: 若是方法区的内存空间不能知足内存分配请求,那Java虚拟机将抛出一个OutOfMemoryError异常。

相关文章
相关标签/搜索