转行学java之前,总是听着大佬们说着java像个渣男一样可以跨平台,一次编译到处运行,瞬间,我就坚定了学java的信念,哎呀妈呀,得劲。真的学java之后,好像渣男也不是那么好学的,尤其这货的必杀技,各年龄段(操作系统)通杀太难了,这可激起了我的小暴脾气,还有能难倒小灏哥的,终于在我兢兢业业的努力下,发现了一个道理,还真有。随着慢慢攻克,看书,看看视频课,也了解了些皮毛,写出来当做个笔记吧。

学习的第一步,就是去oracle官网下载,下载完打开发现有俩目录jdk和jre,这就尴尬了,我要学的不是jvm虚拟机吗,我要学的不是他那处处可留情的必杀技吗,咋越整越多了,这三个东西困扰了我很久,背过,半个月就忘,这可咋整,我可是要当海贼王的男人,怎么能被区区jvm,jdk,jre难倒呢,耳边响起了某位大佬的二字真言:理解。背是行不通了,再花点时间钻研钻研吧。先百度瞅瞅:

jvm:Java Virtual Machine(java虚拟机)

jre:Java Runtime Environment(java运行环境)

jdk:java development kit (java开发工具包)

还是没太搞懂是啥,先看一下java下载下来的jdk和jre有啥吧,先打开jdk,

里面有一些目录和文件,其中一个好像很熟悉的样子-----jre,咦,这个不是在外层目录吗,难道他会瞬移,退回上级目录:

还好,他还在,点开两个jre:

基本上是一样的,我就猜这jdk是不是就是包含jre的呢,不然这oracle官网下载下来的东西,还能有问题?

这时候,大牛们就该读源码了,而我则继续百度,发现了一张图:

这张图应该是比较全面了,我们通常说的jdk是包含jre的,而通常说的jre是包含jvm的,jvm运行在os操作系统之上,而像一些开发工具,则是在这jdk基础之上,做了更多的完善,让我们更方便的去开发代码。

了解了他们的关系之后,再逐一了解他们的功能:

大家应该都知道,jvm是个渣男,他可不只是java的jvm,很多语言都可以在jvm上运行,只要你可以编译成class文件,我都可以任你在我的肉体上运行,它的最大功能也就是解释执行class文件。

jre比起jvm来说,稍微收敛一点,他包含了运行class文件需要的类库,他虽然也渣,但是好歹是知道老婆还是java,只不过背地里在什么系统上鬼混就说不准了。

jdk对于java开发来说,最直接面对的,也是最熟悉的,jdk包含了jre,是我们开发用的,其实没有jdk也能运行程序了,但是作为一个开发,没有jdk,你写的代码不能编译成class也没用。

我们现在也算是知道这三个渣男有啥关系,分别有啥用,对于学习java虚拟机来说,也算是打过招呼了,那么接下来,就先深入了解一下jvm的内存是怎么划分的:

线程私有区域:本地方法栈,虚拟机栈,程序计数器

线程共享区域:方法区,堆,运行时常量池

直观点的可以看下上面的内存区域图,细心点的会发现,这个图比我上面写的多了一个叫直接内存的灰色地带,他可是不被外界承认的备胎,他不属于运行时数据区,但是却又经常会用到他,备胎功底雄厚,后面介绍完了这些正牌,也介绍一下他。

虚拟机栈:

首先来聊一下虚拟机栈,大家都知道,栈的数据结构是先进后出,每个线程都会有自己的一块虚拟机栈,虚拟机栈里面是一个个的栈帧,在每次方法调用时,就会创建一个栈帧并将对应的栈帧压栈,方法执行完出栈,就这样跟着方法调用的节奏频繁的一进一出,在设计的时候,也是考虑了方法调用的模型。

栈帧里面主要包含局部变量表,操作数栈,动态连接和返回地址:

局部变量表: 用来存放八大基本类型和局部变量对象引用的地方,我们在方法执行的时候,传参、定义的局部变量都会存放到这里,供计算时,操作数栈存取,它是一个32位的长度,一般的32位也可以放的下,放不下的,占64位的高低位存放。

操作数栈:在执行计算操作时,会从局部变量表里面获取数据(可以使任意java数据类型)放到操作数栈,然后计算完成再从操作数栈移除,并存入局部变量表。

动态连接:这货有点牛,java从入门到放弃都知道这个-->多态,后面聊动态分派时会详细聊到。

返回地址:正常调用程序计数器的返回地址作为返回,具体操作:

1.恢复局部变量表和操作数栈

2.返回值压入调用者操作数栈

3.调整程序计数器指向下一个命令处

其实这一块理解不够透彻的可以想象一下方法的调用,比如我在一个方法里面调用另一个方法,我在方法调用完成出来之前首先要将这个栈帧的数据清掉,返回值自然是外层方法要用到的了,然后呢,就要调用下一步了,自然要调整程序计数器的指向。

异常返回通过异常处理器表来确定。

程序计数器:

    上面说了,程序计数器是线程私有的,他是记录了当前线程执行的字节码行号地址,由于 Java 是多线程语言,当执行的线程数量超过 CPU 核数时,线程之间会根据时间片轮询争夺 CPU 资源。如果一个线程的时间片用完了,或者是其它原因导致这个线程的 CPU 资源被提前抢夺,那么这个退出的线程就需要单独的一个程序计数器,来记录下一条运行的指令,如果没有程序计数器的保证,线程无法保证当前程序顺序执行。

    相比于普通方法,native方法是特殊的,他的执行是不是jvm具体管理的,所以jvm的程序计数器在执行native方法的时候,一直保持为空,它的调用是操作系统层面的程序计数器来记录的。

 本地方法栈:

      跟虚拟机栈没啥区别,虚拟机栈执行普通方法,本地方法栈执行native方法

上面介绍完了线程私有的jvm内存区域划分,其实对于线程私有的对于天生多线程的java,是否也是一个天生线程安全的方式呢,由jvm自行处理,又不用加锁就可以实现线程安全,我觉得也是我们在设计线程安全需要考虑的方向吧。

接下来开始介绍线程共享的,首先来介绍方法区:

方法区:

       方法区是线程共享的,他主要存储类的结构信息,运行时常量池(1.8以后放到堆里面了,但其实他的定位应该还是属于方法区的范畴),构造函数,方法数据等,总的来说其实就是存放初始化时需要用到的数据。

方法区和永久代以及元空间是很容易搞混的,尤其是永久代和方法区,其实方法区相当于是jvm对内存的逻辑划分,而永久代和元空间是jdk1.7以前和jdk1.81以后分别对方法区的不同实现,我们以前用过jdk1.6版本,公司很抠门,都是2G的内存,使用默认的配置经常会出现 java.lang.OutOfMemoryError: PermGen异常,相信这个异常很多人都很熟悉,其实这个就是永久代内存溢出,而现在,我们一般都用16g内存,而且jdk1.8之后的元空间不设置参数只受本机内存的限制,想要体验一下的同学可以在vm参数加上-XX:MaxMetaspaceSize=1M,就能很快看到OutOfMemoryError: Metaspace,可见两者实现还是不一样的。

看到这里,oracle官方为啥要将永久代弃掉而改用元空间呢,首先按官方来说,是为了融合HotSpot和JRockit,因为JRockit没有永久代,其次的话,永久代必须要配置分配空间,而太小很容易内存溢出,太大又太占空间,为其分配多少内存很难确定(永久代的大小依赖因素太多,如常量池大小,方法的大小,class等)永久代在每次FullGc都可能呗回收,而回收率很低,而元空间存储在本地内存,不需要考虑这么多。

运行时常量池:

        运行时常量池就是用来将class常量池的符号引用转为直接引用。

堆:

      堆是jvm上最大的一块区域,也是我们后面聊到的垃圾回收器GC主要光顾的地方,因为我们几乎所有的对象(几乎是因为会存在栈上分配以及八大基本对象等,但这些毕竟是小部分)都在堆中,堆给人的感觉就是个沙盘,承载了那么多的对象就像是一个个沙兵,而这些对象的引用就像是一根根连接沙兵的线都放在栈上(结合上面说的其实就是放到局部变量表里,这时候就有问题了,局部变量表是线程私有的,那我们常说的线程安全还需要考虑吗,其实就是因为这边存放的是引用,别的线程也拿到一个引用指向这个对象,那还不是随便改),栈就像是个军师一样,在沙盘上指点着干掉这个引用,垃圾回收器回收的时候就会把这些对象给干掉(当然不止这么简单,垃圾回收后面也会细讲)。

直接内存:

        直接内存又叫堆外内存,jvm运行时会首先申请一块块内存,分配给堆、栈、方法区等,而jvm没有申请的内存,就是直接内存,其实就是jvm管不到的地方,很神奇哈,jvm管不到的地方,为啥要了解呢,因为随着java生态的发展,我们不仅仅要用好申请的资源,没申请的有机会也要用好,提升资源的利用率,比如说使用NIO的话,就会频繁用到这一块,可以用java的directByteBuffer 对象操作,也可以用-XX:MaxDirectMemorySize来设置它的大小。

这一篇简单介绍了一下jvm的轮廓,相信看完之后,对jvm大体上有一个全面的了解了吧,jvm是是比较基础的知识,枯燥无味,很多东西看不见,摸不着,平时可能还用不到,但学好java,还是必须要有这方面的基础的,希望看到我文章的有缘人一起学习,共同进步吧。

.tb_button { padding: 1px; cursor: pointer; border-right: 1px solid rgba(139, 139, 139, 1); border-left: 1px solid rgba(255, 255, 255, 1); border-bottom: 1px solid rgba(255, 255, 255, 1) }
.tb_button.hover { borer: 2px outset #def; background-color: rgba(248, 248, 248, 1) !important }
.ws_toolbar { z-index: 100000 }
.ws_toolbar .ws_tb_btn { cursor: pointer; border: 1px solid rgba(85, 85, 85, 1); padding: 3px }
.tb_highlight { background-color: rgba(255, 255, 0, 1) }
.tb_hide { visibility: hidden }
.ws_toolbar img { padding: 2px; margin: 0 }

java虚拟机入门(一)-jvm基础的更多相关文章

  1. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

  2. Java虚拟机内存管理原理基础入门

    Jdk:Java程序设计语言.Java虚拟机.Java API类库. Jdk是用于支持Java程序开发的最小环境. Jre:Java API类库中的Java SE API子集.Java虚拟机. Jre ...

  3. Java虚拟机详解----JVM常见问题总结

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

  4. 【java虚拟机系列】java虚拟机系列之JVM总述

    我们知道java之所以能够快速崛起一个重要的原因就是其跨平台性,而跨平台就是通过java虚拟机来完成的,java虚拟机属于java底层的知识范畴,即使你不了解也不会影响绝大部分人从事的java应用层的 ...

  5. 深入理解java虚拟机(1)走进jvm

    1.JDK:java程序设计语言.java虚拟机.javaAPI 二.自动内存管理机制 ----------------------------------------------------- 1. ...

  6. java虚拟机入门(二)-探索内存世界

    上节简单介绍了一下jvm的内存布局以及简单概念,那么对于虚拟机来说,它是怎么一步步的让我们能执行方法的呢: 1.首先,jvm启动时,跟个小领导一样会根据配置参数(没有配置的话jvm会有默认值)向大领导 ...

  7. java虚拟机参数设置 jvm参数设置

    java进程命令行使用方式如下: java [-options] class [args...] -options 表示虚拟机的启动参数, class为带有main()函数的java类的全名称 arg ...

  8. Java虚拟机详解----JVM内存结构

    http://www.cnblogs.com/smyhvae/p/4748392.htm 主要内容如下: JVM启动流程 JVM基本结构 内存模型 编译和解释运行的概念 一.JVM启动流程: JVM启 ...

  9. 【深入Java虚拟机】一 JVM类加载过程

    首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...

随机推荐

  1. Centos上配置nginx+uwsgi+负载均衡配置

    负载均衡在服务端开发中算是一个比较重要的特性.因为Nginx除了作为常规的Web服务器外,还会被大规模的用于反向代理后端,Nginx的异步框架可以处理很大的并发请求,把这些并发请求hold住之后就可以 ...

  2. 小白都能理解的Python多继承

    本文主要做科普用,在真实编程中不建议使用多重继承,或者少用多重继承,避免使代码难以理解. 方法解析顺序(MRO) 关于多重继承,比较重要的是它的方法解析顺序(可以理解为类的搜索顺序),即MRO.这个跟 ...

  3. 会Python了不起吗?是的,简直开挂

    前段时间听说了一件事,彻底刷新了我对"黑科技"的认知. 有一个小学弟,大学4年混得风生水起,恋爱.赚钱.写论文.找工作,样样都很顺利,简直是妥妥的人生赢家. 问他凭什么?张口就是: ...

  4. 常用Appium API

    以最右App为例 .apk文件网盘地址: 链接:https://pan.baidu.com/s/1L4MYkhpb5ECe8XeaneTx_Q 提取码:0jqm 操作类API # -*- coding ...

  5. 设计模式之-Builder模式

    场景引入: 一个类,如果有多个属性时,在创建对象,如何对属性进行赋值呢? 1.通过构造器赋值,这种方案优点时一次性赋值完成,但是多种属性的组合,导致构造器会非常多. 2.通过setter方法赋值,方案 ...

  6. CentOS7下常用安装服务软件yum方式的介绍

    简介:介绍yum软件包的管理并配置本地yum源 yum安装:基于 C/S 架构,yum安装称之为傻瓜式安装 yum安装优点:方便快捷,不用考虑包依赖,自动下载软件包. yum安装缺点:人为无法干预,无 ...

  7. [leetcode]692. Top K Frequent Words频率最高的前K个单词

    这个题的排序是用的PriorityQueue实现自动排列,优先队列用的是堆排序,堆排序请看:http://www.cnblogs.com/stAr-1/p/7569706.html 自定义了优先队列的 ...

  8. python的22个基本语法

    "人生苦短,我用Python".Python编程语言是最容易学习.并且功能强大的语言.只需会微信聊天.懂一点英文单词即可学会Python编程语言.但是很多人声称自己精通Python ...

  9. Python错误重试方法

    前言 Tenacity是一个 Apache 2.0授权的通用重试库,用 Python 编写,用于简化向几乎所有内容添加重试行为的任务.它起源于一个重新尝试的分支,可惜这个分支已经不复存在了. 使用Te ...

  10. RocketMQ(十):数据存储模型设计与实现

    消息中间件,说是一个通信组件也没有错,因为它的本职工作是做消息的传递.然而要做到高效的消息传递,很重要的一点是数据结构,数据结构设计的好坏,一定程度上决定了该消息组件的性能以及能力上限. 1. 消息中 ...