提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
1. 概述:
从广义上讲,Kotlin、Clojure、JRuby、Groovy等运行于Java虚拟机上的编程语言及其相关的程序都属于Java技术体系中的一员。如果仅从传统意义上来看,JCP官方所定义的Java技术体系包括了以下几个组成部分:
- Java程序设计语言
- 各种硬件平台上的Java虚拟机实现 Class文件格式
- Java类库API(一些Oracle的API等)
- 来自商业机构和开源社区的第三方Java类库(第三方通过类加载器loading到内存的类库)
按照Java各个组成部分的功能来进行划分
可以分为JDK与JRE。
我们可以把Java程序设计语言、Java虚拟机、Java类库这三部分统称为JDK(Java Development Kit)JDK是用于支持Java程序开发的最小环境。
我们可以把Java类库API中的Java SE API子集和Java虚拟机这两部分统称为JRE(Java Runtime Environment),JRE是支持Java程序运行的标准环境。
如果按照技术所服务的领域来划分
,或者按照技术关注的重点业务来划分的话。
那Java技术体系可以分为以下四条主要的产品线:
- Java Card:支持Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。
- Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)上的平台,对Java API有所精简,并加入了移动终端的针对性支持,这条产品线在JDK 6以前被称为J2ME。有一点读者请勿混淆,现在在智能手机上非常流行的、主要使用Java语言开发程序的Android并不属于Java ME。
- Java SE(Standard Edition):支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,这条产品线在JDK 6以前被称为J2SE。
- Java EE(Enterprise Edition):支持使用多层架构的企业应用(如ERP、MIS、CRM应用)的Java平台,除了提供Java SE API外,还对其做了大量有针对性的扩充,并提供了相关的部署支持,这条产品线在JDK 6以前被称为J2EE,在JDK 10以后被Oracle放弃,捐献给Eclipse基金会管理,此后被称为Jakarta EE。
2.Java从编码到执行
在C语言和C++是使用过程中内存分配了一块区域之后,是需要手动分配内存的使用。
从 ClassLoader到执行引擎就是我们的JVM
从上图可以看出来Java执行到底是编译执行还是解释执行?
其实解释和编译是可以混合的,像一些常用的代码且用到的次数比较多,他会把代码的及时编译做成本地编译,下次在执行这种重复的代码的时候就不需要去通过字节码的解释器去进行解释操作,它会直接通过即时编译器编译交给执行引擎进行执行。
3.Java与JVM
Java是跨平台的语言,而JVM是跨语言的平台,它与Java无关,它只面向与字节码文件,也就是我们Javac编译的字节码文件,所以说,JVM也是一种规范,任何语言只要满足class规范就可以在JVM虚拟机进行执行。
JVM屏蔽了与具体操作系统平台相关的信息,使得JAVA程序只需生成在JAVA虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
4.JVM是什么?
JVM是Java Virtual Machine(Java虚拟机)的缩写。
虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码class文件),就可以在多种平台上不加修改地运行。简单来说JVM是用来解析和运行Java程序的。
什么是JVM
Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。
一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码class文件),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够**“一次编译,到处运行”**的原因。
运行时数据区域
程序计数器
程序计数器是线程独有的空间
。Java虚拟机可以同时执行多个线程,但在任何一个确定的时刻,一个处理器只会执行一个线程中的一个指令,又因为线程具有随机性,操作系统会一直切换执行不同指令,我们需要把切换时候线程执行的位置存入到PC寄存器中,等切回来的时候能够回到原来的位置继续执行。
任何时候,每个Java虚拟机都在执行单个方法的代码,即该线程的当前方法。如果该方法不是Native方法,即PC寄存器会记录当前正在执行的java虚拟机指令的地址,如果线程当前执行的方法是本地的,那么java虚拟机的PC寄存器的值就是Undefined。 程序计数器是唯一一个不会出现OOM的区域。
堆
堆是java虚拟机管理内存最大的一块,在虚拟机启动时创建,所有线程共享,堆中的对象永远不会被显式释放,必须由GC回收,所以GC也主要回收堆中的对象实例
,我们平常讨论的垃圾回收就是回收堆内存。堆可以处于物理上不连续的空间,可以固定大小,也可以动态扩展,通过参数-Xms和-Xmx两个参数控制堆的最小值和最大值。
方法区
方法区也是线程共享的区域,在虚拟机启动时创建,存储每个类的结构
,比如:运行时常量池、属性和方法数据,以及方法和构造函数的代码,包括在类和实例初始化以及接口初始化使用的特殊方法,方法区在逻辑上是堆的一部分,但是它又有另一个别名叫非堆,目的是与堆区分开。方法区可以是固定大小,也可以根据计算需要进行扩展。如果方法区的内存无法满足分配请求时也会抛出OutOfMemoryError。
运行时常量池
方法区的一部分,用于存储编译生成的字面量(基本数据类型或被final修饰的常量或字符串)和符号引用,类或接口的运行时常量池是在java虚拟机创建类或接口时创建的
。 在jdk1.6以及之前的版本,Java中的字符串是放在方法区中的运行时常量池内,但是在jdk1.7以后将字符串常量池拿出来放在了堆中。
内存异常
内存溢出
- 概念
JVM内存不够了,,目前无法存放创建的对象 - 原因
JVM分配的内存太小,也可能是服务器本身内存太小,也许是JVM分配的堆内存太小
某段代码死循环,导致疯狂创建对象,但又不会触发GC
创建的对象太大,导致新生代存不下,老年代也存不下,只能OOM了。 - 解决
增加服务器内存,设置合理的-Xms和-Xmx
通过线程dump和堆dump,分析出现问题的代码
增大JVM内存,及时GC
5.HotSpot 虚拟机
首先打开cmd,输入java-version可以看到我们的虚拟机的名称是HotSpot 虚拟机 64位的 Server版 ,模式是解释执行与编译执行混合的即mixed mode
我们使用的jdk 的版本是1.8.0 如下:
浅谈HotSpot?
Sun公司的jdk版本是从1.3.1开始使用HotSpot 虚拟机,2006年底开源,主要是使用C++进行实现,JNI接口用C进行实现,Java原先代码是编译成字节码文件放在虚拟机上进行执行,但是只有执行比较慢,但是HotSpot将常用代码编译成本地代码极大提高性能。
HotSpot 虚拟机包括一个解释器与两个编译器(client与server两个二选一)解释编译混合模式两个是二选一的,默认是启动解释执行。
server启动慢,占用内存多,执行效率高,适用于服务端应用。
client启动快,占用内存少,执行效率没有server快,默认情况下不进行动态编译,适用于桌面应用程序。
像有些公司会有自己一套虚拟机,部分虚拟机是可以直接针对硬件的,这样的执行效率比针对与操作系统的就高很多。