1. JVM是什么

  JVM是Java Virtual Mechine的缩写。它是一种基于计算设备的规范,是一台虚拟机,即虚构的计算机。

  JVM屏蔽了具体操作系统平台的信息(显然,就像是我们在电脑上开了个虚拟机一样),当然,JVM执行字节码时实际上还是要解释成具体操作平台的机器指令的。

  通过JVM,Java实现了平台无关性,Java语言在不同平台运行时不需要重新编译,只需要在该平台上部署JVM就可以了。因而能实现一次编译多处运行。(就像是你的虚拟机也可以在任何安了VMWare的系统上运行)

  2. JRE和JDK

  JRE:Java Runtime Environment,也就是JVM的运行平台,联系平时用的虚拟机,大概可以理解成JRE=虚拟机平台+虚拟机本体(JVM)。类似于你电脑上的VMWare+适用于VMWare的Ubuntu虚拟机。这样我们也就明白了JVM到底是个什么。

  JDK:Java Develop Kit,Java的开发工具包,JDK本体也是Java程序,因此运行依赖于JRE,由于需要保持JDK的独立性与完整性,JDK的安装目录下通常也附有JRE。目前Oracle提供的Windows下的JDK安装工具会同时安装一个正常的JRE和隶属于JDK目录下的JRE。

  3. JVM结构

  JVM主要包括:程序计数器(Program Counter),Java堆(Heap),Java虚拟机栈(Stack),本地方法栈(Native Stack),方法区(Method Area)

  详细的结构如下:

  

  现在我来分别介绍一下每一部分的功能。

  3.1. 程序计数器(PC, Program Counter)

  PC是一个寄存器,可以看作是代码行号指示器,类似于实际计算机里的PC,用于指示,跳转下一条需要执行的命令。Java的基础操作以及异常处理等都十分依赖PC。

  JVM多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的。在一个确定的时刻,一个处理器(或者说多核处理器的一个内核)只会执行一条线程中的命令。因此,为了正常的切换线程,每个线程都会有一个独立的PC,各线程的PC不会互相影响。这个私有的PC所占的这块内存即是线程的“私有内存”。

  如果线程在执行的是Java方法,那么PC记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的不是Java方法即Native方法,那么PC的值为undefined。

  PC的内存区域是唯一的没有规定任何OutOfMemoryError的Java虚拟机规范中的区域。

  3.2. Java虚拟机栈(Stack,Java Virtual Mechine Stacks)

  同PC一样(从工作流程图里我们可以看到,实际上,PC也是存在于JVM Stack上的),也是线程私有的,生命周期与线程相同。虚拟机栈描述Java方法执行的内存模型,每个方法被执行时都会创建一个栈帧(Stack Frame),栈帧会利用局部变量数组存储局部变量(Local Variables),操作栈(Operand Stack),方法出口(Return Value),动态连接(Current Class Constant Pool Reference)等信息。

  局部变量数组存储了编译可知的八个基本类型(int, boolean, char, short, byte, long, float, double),对象引用(根据不同的虚拟机实现可能是引用地址的指针或者一个handle),returnAddress类型。64位的long和double会占用两个Slot,其余类型会占用一个Slot。在编译期间,局部变量所需的空间就会完成分配,动态运行期间不会改变所需的空间。

  操作栈在执行字节码指令时会被用到,这种方式类似于原生的CPU寄存器,大部分JVM把时间花费在操作栈的花费上,操作栈和局部变量数组会频繁的交换数据。

  动态连接控制着运行时常量池和栈帧的连接。所有方法和类的引用都会被当作符号的引用存在常量池中。符号引用是实际上并不指向物理内存地址的逻辑引用。JVM 可以选择符号引用解析的时机,一种是当类文件加载并校验通过后,这种解析方式被称为饥饿方式。另外一种是符号引用在第一次使用的时候被解析,这种解析方式称为惰性方式。无论如何 ,JVM 必须要在第一次使用符号引用时完成解析并抛出可能发生的解析错误。绑定是将对象域、方法、类的符号引用替换为直接引用的过程。绑定只会发生一次。一旦绑定,符号引用会被完全替换。如果一个类的符号引用还没有被解析,那么就会载入这个类。每个直接引用都被存储为相对于存储结构(与运行时变量或方法的位置相关联的)偏移量。

  对Java虚拟机栈这个区域,Java虚拟机规范规定了两种异常:

  线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverFlow异常。

  对于支持动态扩展的虚拟机,当扩展无法申请到足够的内存时会抛出OutOfMemory异常。

  3.3. 本地方法栈(Native Stack)

  本地方法栈如其名字,和Java Virtual Machine Stack其实极为类似,只是执行的是Native方法,为Native方法服务。在JVM规范中,没有对它的实现做具体规定。

  3.4.Java 堆(Heap, Garbage Collection Heap)

  

  Java堆是被所有线程共享的一块区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存(随着技术的发展,已不绝对)。

  Java堆是垃圾收集器管理的主要区域,因而也被称为GC堆。收集器采用分代回收法,GC堆可以分为新生代(Yong Generation)和老生代(Old Generation)。新生代包括Eden Space和Survivor Space。但无论哪个区域,如何划分,存储的都是Java对象实例,进一步的划分是为了更好的回收内存或快速的分配内存。

  根据Java虚拟机规范,堆所在的物理内存区间可以是不连续的,只要逻辑连续就可以。实现时既可以是固定大小,也可以是可扩展的。如果堆无法扩展时,就会抛出OutOfMemoryError。

  3.5. 方法区(Method Area)

  方法区和Java堆类似,也属于各线程共享的内存区域。用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码数据等。它属于非堆区(Non Heap),和Java堆区分开。对于存在永久代(Permanent)概念的虚拟机(HotSpot)而言,方法区存在于永久代。Java虚拟机规范对方法区的规定很宽松,甚至可以不实现GC。不过并非进入方法区的数据就会永久存在了,这块区域的内存回收主要为常量池的回收和类型的卸载。这个区域的回收处理不善也会导致严重的内存泄漏。

  当方法区无法满足内存分配需求时也会抛出OutOfMemoryError。

  3.6. 代码缓存(Code Cache)

  用于编译和存储那些被 JIT 编译器编译成原生代码的方法。

  3.7. 类信息(Class Data)

  类信息存储在方法区,其主要构成为运行时常量池(Run-Time Constant Pool)和方法(Method Code)。

  一个编译后的类文件包括以下结构:

  

  3.8. 运行时常量池(Run-Time Constant Pool)

  运行时常量池是方法区的一部分。Class文件中有类的版本,字段,方法,接口等描述信息和用于存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后存放到方法区的运行时常量池中。Java虚拟机规范对Class的细节有着严苛的要求而对运行时常量池的实现不做要求。一般来说除了翻译的Class,翻译出来的直接引用也会存在运行时常量池中。

  运行时常量池具备动态性,即运行时也可将新的常量放入池中。比如String类的intern()方法。

  常量池无法申请到足够的内存分配时也会抛出OutOfMemoryError。

  3.9. 直接内存(Direct Memory)

  直接内存并不在Java虚拟机规范中,不是Java的一部分,但是也被频繁使用并可能导致OutOfMemoryError。Native函数库可以直接分配堆外内存,通过存储在Java堆里的DirectDataBuffer对象作为这块内存的引用进行操作。这样做在一些场景中可以显著提高性能。

  直接内存是堆外内存,自然不受Java堆大小的限制,但是可能受实体机内存大小的限制。如果内存各部分总和大于实体机的内存时,也会报出OutOfMemoryError。

  4. Java垃圾回收

  将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停。

  不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:

  强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)。

  软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)。

  弱引用:在GC时一定会被GC回收。

  虚引用:由于虚引用只是用来得知对象是否被GC。

  5. JVM线程与原生线程的关系

  JVM允许一个程序使用多个并发线程,Hotspot JVM中Java的线程与原生操作系统的线程是直接映射关系。即当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作系统原生线程。Java 线程结束,原生线程随之被回收。操作系统负责调度所有线程,并把它们分配到任何可用的 CPU 上。当原生线程初始化完毕,就会调用 Java 线程的 run() 方法。run() 返回时,被处理未捕获异常,原生线程将确认由于它的结束是否要终止 JVM 进程(比如这个线程是最后一个非守护线程)。当线程结束时,会释放原生线程和 Java 线程的所有资源。

JAVA基础知识详解的更多相关文章

  1. 【干货】用大白话聊聊JavaSE — ArrayList 深入剖析和Java基础知识详解(二)

    在上一节中,我们简单阐述了Java的一些基础知识,比如多态,接口的实现等. 然后,演示了ArrayList的几个基本方法. ArrayList是一个集合框架,它的底层其实就是一个数组,这一点,官方文档 ...

  2. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

  3. RabbitMQ基础知识详解

    什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取队列中 ...

  4. Cisco路由技术基础知识详解

    第一部分 请写出568A的线序(接触网络第一天就应该会的,只要你掐过,想都能想出来) .网卡MAC地址长度是(  )个二进制位(16进制与2进制的换算关系,只是换种方式问,不用你拿笔去算) A.12  ...

  5. Python基础知识详解 从入门到精通(七)类与对象

    本篇主要是介绍python,内容可先看目录其他基础知识详解,欢迎查看本人的其他文章Python基础知识详解 从入门到精通(一)介绍Python基础知识详解 从入门到精通(二)基础Python基础知识详 ...

  6. Java网络编程一:基础知识详解

    网络基础知识 1.OSI分层模型和TCP/IP分层模型的对应关系 这里对于7层模型不展开来讲,只选择跟这次系列主题相关的知识点介绍. 2.七层模型与协议的对应关系 网络层   ------------ ...

  7. 直播一:H.264编码基础知识详解

    一.编码基础概念 1.为什么要进行视频编码? 视频是由一帧帧图像组成,就如常见的gif图片,如果打开一张gif图片,可以发现里面是由很多张图片组成.一般视频为了不让观众感觉到卡顿,一秒钟至少需要16帧 ...

  8. Redis基础知识详解(非原创)

    文章大纲 一.Redis介绍二.Redis安装并设置开机自动启动三.Redis文件结构四.Redis启动方式五.Redis持久化六.Redis配置文件详解七.Redis图形化工具八.Java之Jedi ...

  9. Maven基础知识详解

    1. 简介   Maven在Java领域的应用已经非常广泛了,有了Maven的存在是的开发人员在搭建.依赖.扩展和打包项目上变得非常简单. 2. Windows安装Maven 下载安装包   http ...

随机推荐

  1. java类的成员初始化顺序和初始化块知识

    java类的成员初始化顺序和初始化块知识 转自:http://blog.csdn.net/lgfeng218/article/details/7606735 属性.方法.构造方法和自由块都是类中的成员 ...

  2. OC开发_Storyboard——多线程、UIScrollView

    一.多线程 1.主队列:处理多点触控和所有UI操作(不能阻塞.主要同步更新UI) dispatch_queue_t mainQueue = dispatchg_get_main_queue(); // ...

  3. MyBatis DAO层传递参数到mapping.xml 几种方式

    Dao层传递参数到mapping.xml文件的几种方式:(Mybatis传值总结) 第一种:传递单个参数 Dao层Code片段: /** * 根据articleId查询XXXX详情. * * @par ...

  4. apidemos编译出错

    编译api 19的(4.4.2)apidemos一直报xml相关资源出错. 把build-toos 22删除,替换成19版本的,就ok了. 真是坑啊. 学习新东西难免遇到坑.

  5. JS实现数字千位符格式化方法

    /** * [number_format 参数说明:] * @param {[type]} number [number:要格式化的数字] * @param {[type]} decimals [de ...

  6. Python开发【项目】:博客后台

    概述 通过自己写的博客后台代码.思路,来与武sir的代码进行一个差异化的比较,记录之间的差距,改善以后写代码的思路 博客后台这个项目,对之前Django学习的各个知识点都有涉及到,非常重要 用户登录验 ...

  7. IntelliJ IDEA 插件

    alibaba java  coding guidelines 阿里巴巴Java编码指南插件支持. Mybatis-log-plugin 把 mybatis 输出的sql日志还原成完整的sql语句. ...

  8. P2750 贰五语言Two Five USACO5.5 记忆化搜索

    正解:记搜+逼近 解题报告: 神仙题预警,,, 我真滴觉得还是挺难的了,,, 大概说下思路趴QAQ 首先我们要知道逼近法是什么! 逼近法,有点像二分的思路,以这题为例举个eg 假如它给了个数字k.我们 ...

  9. 11.Git分支管理

    分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN. 如果两个平行宇宙互不干扰,那对现在的你也没啥影响.不过,在某个时间点,两个平行宇宙合并 ...

  10. 103-advanced-上下文

    上下文提供了一种通过组件树传递数据的方法,无需在每个级别手动传递道具. 在典型的React应用程序中,数据通过prop自上而下(父到子)传递,但对于应用程序中许多组件所需的某些类型的道具(例如场所偏好 ...