java程序运行

*.java文件-->编译器-->*.class文件-->线程启动(main)-->jvm-->操作系统-->硬件

通过上面的流程我们可以看出java程序的执行顺序,那么jvm到底是什么,class文件到底是如何在jvm中运行就显得很重要了。

jvm原理

什么是jvm

openjdk源码地址http://hg.openjdk.java.net/jdk9

JVM是一个计算机模型,JVM对Java可执行代码,即字节码(Bytecode)的格式给出了明确的规格。这一规格包括操作码和操作数的语法(也就是cpu指令集)和数值、标识符的数值表示方式、以及Java类文件中的Java对象、常量缓冲池在JVM的存储映象。

JVM的组成

JVM指令系统、JVM寄存器、JVM 栈结构、JVM 碎片回收堆、JVM 存储区

JVM指令

Java指令也是由操作码和操作数两部分组成,与RISC CPU采用的编码方式是一致的,也就是精简指令集,目前UNIX、Linux、MacOS系统使用RISC,我们目前知道的x86架构CPU使用的CISC编码,也就是复杂指令集。

JVM寄存器

1.pc程序计数器

2.optop操作数栈顶指针

3.frame当前执行环境指针

4.vars指向当前执行环境中第一个局部变量的指针

jvm的装载

windows操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境。

1.创建JVM装载环境和配置

2.装载JVM.dll(C:\Program Files\Java\jre1.8.0_151\bin\server linux在jre/lib/server下)

3.初始化JVM.dll并挂接到JNIENV(JNI调用接口)实例

4.调用JNIEnv实例装载并处理class类。

JVM虚拟机相当于x86计算机系统,Java解释器相当于x86CPU

JVM运行数据

JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些数据在JVM启动的时候创建,在JVM退出的时候销毁。而其他的数据依赖于每一个线程,在线程创建时创建,在线程退出时销毁。分别有程序计数器,堆,栈,方法区,运行时常量池

  • 程序计数器:每个线程一旦被创建就拥有了自己的程序计数器。当线程执行Java方法的时候,它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法,那么程序计数器的值就不会被定义。
  • 常量缓冲池和方法区:常量缓冲池用于存储类名称、方法和字段名称以及串常量。方法区则用于存储Java方法的字节码。对于这两种存储区域具体实现方式在JVM规格中没有明确规定。这使得Java应用程序的存储布局必须在运行过程中确定,依赖于具体平台的实现方式。
  • 栈:Java栈是JVM存储信息的主要方法。当JVM得到一个Java字节码应用程序后,便为该代码中一个类的每一个方法创建一个栈框架,以保存该方法的状态信息。每个栈框架包括以下三类信息:

    局部变量:对应vars寄存器指向该变量表中的第一个局部变量,用于存储一个类的方法中所用到的局部变量。

    执行环境:对应frame寄存器的当前执行环境指针,用于保存解释器对Java字节码进行解释过程中所需的信息。它们是:上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针。执行环境是一个执行一个方法的控制中心。例如:如果解释器要执行iadd(整数加法),首先要从frame寄存器中找到当前执行环境,而后便从执行环境中找到操作数栈,从栈顶弹出两个整数进行加法运算,最后将结果压入栈顶。

    操作数栈:对应optop寄存器的操作数栈顶指针,操作数栈用于存储运算所需操作数及运算的结果。

  • 堆:JVM中最大的,应用的对象和数据都是存在这个区域,这块区域也是线程共享的,也是 gc 主要的回收区,一个 JVM 实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行。

       垃圾回收机制(GC)只发生在线程共享区,也就是堆和方法区,栈不需要回收,线程销毁则栈也销毁,         也就是上图的heap space与method area会发生gc。

        通过上图可以发现heap space被分为两部分:

  • Young Generation:又分为Eden space所有的类都是在Eden space被new出来的。From区(Survivor 0 space)和To区(Survivor 1 space)。当Eden space空间用完时,程序又需要创建对象,JVM的垃圾回收器将对Eden space进行垃圾回收(Minor GC),将Eden space中的剩余对象移动到From区。若From区也满了,再对该区进行垃圾回收,然后移动到To区。那如果To区也满了呢,再移动到Old区。
  • Old Generation:若该区也满了,那么这个时候将产生Major GC(FullGCC),进行Tenured区的内存清理。若该区执行Full GC 之后发现依然无法进行对象的保存,产生异常java.lang.OutOfMemoryError: Java heap space。
  1. Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
  2. 代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。
  • Permanent Generation:是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。产生异常java.lang.OutOfMemoryError: PermGen space jdk1.8之后已经不会再报报这个错误了。因为类信息的卸载几乎很少发生,这样会影响GC的效率。于是PermGen便被拆分出去了。
  1. 程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。
  2. 大量动态反射生成的类不断被加载,最终导致Perm区被占满。

jvm的算法

由于算法篇幅太长具体算法可自行查阅资料,主要介绍gc算法发生在什么区。

分代搜集算法:是由复制算法、标记/整理、标记/清除算法共同组成

复制算法发生在Young Generation

标记/整理和标记/清除算法发生在Old Generation和Permanent Generation

java验证jvm

栈中一般存放的都是对象的指针和基本类型,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

栈数据可以共享

​
/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args) {int a=0;int b=0;System.out.print(a==b);}
}​
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
true
Process finished with exit code 0

编译器先处理int a = 0;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为0的地址,没找到,就开辟一个存放0这个字面值的地址,然后将a指向0的地址。接着处理int b = 0;在创建完b的引用变量后,由于在栈中已经有0这个字面值,便将b直接指向0的地址。这样,就出现了a与b同时均指向0的情况

/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args) {int a=0;int b=0;a=1;System.out.print("a="+a);System.out.print("b="+b);System.out.print(a==b);}
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
a=1b=0false
Process finished with exit code 0

再令a=1;那么,b不会等于1,还是等于0。在编译器内部,遇到a=1;时,它就会重新搜索栈中是否有1的字面值,如果没有,重新开辟地址存放1的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

String str = "abc"的工作原理

/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args) {String str1 = "abc";String str2 = "abc";System.out.println(str1==str2);}
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
trueProcess finished with exit code 0
/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args) {String str1 = "abc";String str2 = "abc";str1 = "bcd";System.out.println(str1 + "," + str2);System.out.println(str1==str2);}
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
bcd,abc
falseProcess finished with exit code 0

赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。

/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args) {String str1 = "abc";String str2 = "abc";str1 = "bcd";String str3 = str1;System.out.println(str3);String str4 = "bcd";System.out.println(str1 == str4);}
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
bcd
trueProcess finished with exit code 0

str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用 str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。

堆验证

String类

/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args) {String str1 = new String("abc");String str2 = "abc";System.out.println(str1==str2);}
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
falseProcess finished with exit code 0

 以上代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。

使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是 享元模式的思想。

由于String类的性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

执行时间上寄存器 < 堆栈 < 堆

/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args) {String s1 = "ja";String s2 = "va";String s3 = "java";String s4 = s1 + s2;System.out.println(s3 == s4);System.out.println(s3.equals(s4));}
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java"
false
trueProcess finished with exit code 0

是不是很矛盾啊!是不是又懵逼了?

打印false的原因是,java 重载了“+”,查看java字节码可以发现“+”其实是调用了StringBuilder 所以使用了“+”其实是生成了一个新的对象。所以(s3 == s4)打印false

/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args){long maxMemory = Runtime.getRuntime().maxMemory();//返回Java虚拟机试图使用的最大内存量。Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm实例占用的内存。System.out.println("MAX_MEMORY ="+maxMemory +"(字节)、"+(maxMemory/(double)1024/1024) + "MB");System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字节)"+(totalMemory/(double)1024/1024) + "MB");}
}
"C:\Program Files\Java\jdk1.8.0_151\bin\java" -XX:+PrintGCDetails
MAX_MEMORY =1868038144(字节)、1781.5MB
TOTAL_ MEMORY = 126877696(字节)121.0MB
HeapPSYoungGen      total 37888K, used 3932K [0x00000000d6400000, 0x00000000d8e00000, 0x0000000100000000)eden space 32768K, 12% used [0x00000000d6400000,0x00000000d67d7320,0x00000000d8400000)from space 5120K, 0% used [0x00000000d8900000,0x00000000d8900000,0x00000000d8e00000)to   space 5120K, 0% used [0x00000000d8400000,0x00000000d8400000,0x00000000d8900000)ParOldGen       total 86016K, used 0K [0x0000000082c00000, 0x0000000088000000, 0x00000000d6400000)object space 86016K, 0% used [0x0000000082c00000,0x0000000082c00000,0x0000000088000000)Metaspace       used 3325K, capacity 4494K, committed 4864K, reserved 1056768Kclass space    used 363K, capacity 386K, committed 512K, reserved 1048576KProcess finished with exit code 0

将jvm堆初始值改小,触发gc回收

import java.util.Random;/*** Created by liustc on 2018/4/20.*/
public class JvmTest {public static void main(String[] args){long maxMemory = Runtime.getRuntime().maxMemory();//返回jvm试图使用的最大内存量。Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm实例的内存大小。System.out.println("MAX_MEMORY ="+maxMemory +"(字节)、"+(maxMemory/(double)1024/1024) + "MB");System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字节)"+(totalMemory/(double)1024/1024) + "MB");String str = "www.baidu.com";while(true){str += str + new Random().nextInt(88888888) + new Random().nextInt(99999999);}}}
"C:\Program Files\Java\jdk1.8.0_151\bin\java" -XX:+PrintGCDetails
MAX_MEMORY =1868038144(字节)、1781.5MB
TOTAL_ MEMORY = 126877696(字节)121.0MB
[GC (Allocation Failure) [PSYoungGen: 32247K->2729K(37888K)] 32247K->10124K(123904K), 0.0045031 secs] [Times: user=0.01 sys=0.03, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 32912K->4469K(70656K)] 40307K->26638K(156672K), 0.0121112 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 66160K->776K(70656K)] 88329K->59879K(156672K), 0.0141096 secs] [Times: user=0.03 sys=0.02, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 776K->0K(70656K)] [ParOldGen: 59103K->37630K(116224K)] 59879K->37630K(186880K), [Metaspace: 3408K->3408K(1056768K)], 0.0143902 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 60370K->0K(70656K)] [ParOldGen: 96726K->74565K(172032K)] 157096K->74565K(242688K), [Metaspace: 3409K->3409K(1056768K)], 0.0598124 secs] [Times: user=0.08 sys=0.00, real=0.06 secs]
[GC (Allocation Failure) [PSYoungGen: 60382K->32K(95744K)] 1257771K->1226968K(1463808K), 0.0227293 secs] [Times: user=0.06 sys=0.01, real=0.02 secs]
[GC (Allocation Failure) [PSYoungGen: 32K->32K(131584K)] 1226968K->1226968K(1499648K), 0.0037586 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 32K->0K(131584K)] [ParOldGen: 1226936K->355271K(483840K)] 1226968K->355271K(615424K), [Metaspace: 3409K->3409K(1056768K)], 0.1616835 secs] [Times: user=0.19 sys=0.09, real=0.16 secs]
[GC (Allocation Failure) [PSYoungGen: 2499K->32K(158208K)] 1303306K->1300838K(1526272K), 0.0037952 secs] [Times: user=0.06 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 32K->32K(158208K)] 1300838K->1300838K(1526272K), 0.0036491 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 32K->0K(158208K)] [ParOldGen: 1300806K->473463K(622080K)] 1300838K->473463K(780288K), [Metaspace: 3409K->3409K(1056768K)], 0.1641897 secs] [Times: user=0.30 sys=0.06, real=0.16 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(250880K)] 946230K->946230K(1618944K), 0.0027229 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] 946230K->946230K(1626624K), 0.0027747 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] [ParOldGen: 946230K->709846K(879104K)] 946230K->709846K(1137664K), [Metaspace: 3409K->3409K(1056768K)], 0.1013768 secs] [Times: user=0.28 sys=0.02, real=0.10 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(353280K)] 709846K->709846K(1721344K), 0.0049384 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
[PSYoungGen: 0K->0K(353280K)] [ParOldGen: 709846K->709816K(900608K)] 709846K->709816K(1253888K), [Metaspace: 3409K->3409K(1056768K)], 0.1792920 secs] [Times: user=0.39 sys=0.00, real=0.18 secs]
Heapat java.util.Arrays.copyOf(Arrays.java:3332)PSYoungGen      total 353280K, used 14028K [0x00000000d6400000, 0x00000000ec700000, 0x0000000100000000)at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)eden space 352768K, 3% used [0x00000000d6400000,0x00000000d71b3070,0x00000000ebc80000)at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)from space 512K, 0% used [0x00000000ec680000,0x00000000ec680000,0x00000000ec700000)at java.lang.StringBuilder.append(StringBuilder.java:208)to   space 4608K, 0% used [0x00000000ebe00000,0x00000000ebe00000,0x00000000ec280000)at JvmTest.main(JvmTest.java:15)ParOldGen       total 1368064K, used 709816K [0x0000000082c00000, 0x00000000d6400000, 0x00000000d6400000)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)object space 1368064K, 51% used [0x0000000082c00000,0x00000000ae12e1e8,0x00000000d6400000)Metaspace       used 3440K, capacity 4494K, committed 4864K, reserved 1056768Kat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)class space    used 377K, capacity 386K, committed 512K, reserved 1048576Kat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)Process finished with exit code 1

转载于:https://my.oschina.net/u/3829817/blog/1798201

jvm原理解析--不疯魔不成活相关推荐

  1. jvm原理解析——不疯魔不成活

    摘要: 作为一名java开发人员,如果有人问你java是什么?java是如何运行的?你该如何回答,事实上java是有Java语言.class文件.jvm.Java API共同组成. java程序运行 ...

  2. jvm原理解析——不疯魔不成活 1

    摘要: 作为一名java开发人员,如果有人问你java是什么?java是如何运行的?你该如何回答,事实上java是有Java语言.class文件.jvm.Java API共同组成. java程序运行 ...

  3. 什么是JVM?深入解析JVM原理!

    一.JVM是什么? JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.由一套字节码指令集.一组寄存器.一个栈.一个垃圾回收 ...

  4. Tomcat 架构原理解析到架构设计借鉴

    ‍ 点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 Tomcat 架构原理解析到架构设计借鉴 Tomcat 发展这 ...

  5. 6、HIVE JDBC开发、UDF、体系结构、Thrift服务器、Driver、元数据库Metastore、数据库连接模式、单/多用户模式、远程服务模式、Hive技术原理解析、优化等(整理的笔记)

    目录: 5 HIVE开发 5.1 Hive JDBC开发 5.2 Hive UDF 6 Hive的体系结构 6.2 Thrift服务器 6.3 Driver 6.4 元数据库Metastore 6.5 ...

  6. jmap命令的实现原理解析

    本文来说下jmap命令的实现原理解析 文章目录 概述 jmap可以做什么 jmap实现原理 attach SA 本文小结 概述 当服务器端发生GC问题的时候,应该怎样去处理GC问题.其中jmap就是一 ...

  7. Java类加载原理解析(转)

    1       基本信息 摘要: 每个java开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载.Java的类加 ...

  8. 代理模式的原理解析入门

    什么是代理模式? 它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能. 我们通过一个简单的例子来解释一下这段话. 这个例子来自我们在第 25.26.39.40 节中讲的性能 ...

  9. Java 虚拟机(JVM)原理介绍

    Java 虚拟机[JVM]原理介绍 1.概述 2.Java类的加载原理机制 2.1 .Java类的加载过程 2.2 .Class loader (类加载器) 2.2.1 类的生命周期 2.2.1.1 ...

最新文章

  1. 原理暂且不谈,定时器你当真会用?
  2. 计算一个二进制数中数字“1”的个数(位运算)
  3. eacharts中国地图省市区点击显示
  4. 灵魂一问-如何彻底防止APK反编译?成功定级腾讯T3-2
  5. 终于有人把Knative讲明白了
  6. c++ 删除文件_win7 系统电脑C盘哪些文件可以删除掉?
  7. Linux下配置两个或多个Tomcat启动
  8. 快速对表的某字段赋递增的数值
  9. Eclipse汉化教程2021新版
  10. 程序员应该阅读的一些书籍
  11. 从互联网大厂跳槽到国企后,我发现没有一劳永逸的工作。。。
  12. 全民打怪兽全民战场恐惧流伙伴实测
  13. 在 html 中用加色法混合颜色,加色混合是()的混合
  14. 刻薄是因为底子薄,尖酸是因为心里酸
  15. 圆的css样式,圆形进度条css3样式
  16. 中微子探测地幔放射性生热
  17. AE插件-快速景深模糊插件 Aescripts Fast Bokeh Pro v2.0.7 WIN
  18. 官宣|深圳新增6条新建地铁线路和100个站点
  19. 于我而言,大早上的好消息——JetBrains Fleet来了
  20. openlayers根据半径绘制圆形,多圆连线并标记距离

热门文章

  1. 第11课:生活中的组合模式——自己组装电脑
  2. 不插电的计算机科学什么意思,请用“不插电的计算机科学”的思想方法,将二进制数(01110100)2快速.._简答题试题答案...
  3. 武陵源计算机学校,武陵源-张家界市武陵源区武陵源旅游指南[组图]
  4. mc服务器领地位置,服务器领地amp;地皮指令大全 183独家汇总_18183我的世界专区...
  5. Android开发(一)-发展和历史
  6. ccf论文分级_论文等级如何划分
  7. 软件的版本Alpha Beta RC Build等到底是什么意思?
  8. 学校计算机室应该配备哪种灭火器,学校教学楼应配备哪种灭火器
  9. cl.fe3.xyz index.php,2_FE_Diabetes.ipynb
  10. java二维数奇数组金字塔_金字塔内神秘的数字~世界末日真的存在?