带着新人看java虚拟机03
分享一篇博客:https://blog.csdn.net/yfqnihao/article/details/8289363,本篇有部分参考这篇博客!!!
还是继续说一下java虚拟机,为什么呢?因为我随意翻着别人的博客一不小心看到有关jvm的一点新的东西,挺有趣的,就按照我的理解分享一下;
还记得以前学过一首诗,“看成岭侧成峰,远近高低各不同”,这一句诗的内在含义有的时候真的会让你猛然惊醒,进而如获至宝!的确,有的时候换一个角度看问题,你会发现不一样的世界。
我们平常学java的时候肯定涉及到了进程,多线程的概念,但是有没有想过操作系统也有进程和线程的概念,两者有关系吗?假如我们视角放高一点,以操作系统的角度看看一个java程序的运行,又会是什么样子的呢?jvm在将字节码文件翻译成机器码之后怎么会调用cpu呢?自己调用的还是假借了谁的手呢?jvm在操作系统中到底扮演着一个什么角色呢?还有最基本的一个问题,操作系统是什么?
下面我们就来把这些东西整个的给串一下,当然,具体的细节还要每个人自己去研究;
1.简单看看操作系统
水平有限,不可能对操作系统理解得那么透彻,只是说说我自己的理解吧!
一说起操作系统大家肯定既陌生又贼熟悉,为什么呢?因为我们经常使用操作系统,比如windows系列,unix系列,macos等等,我们每天一打开电脑首先就会自动启动一个操作系统,但是又有几个人真的能了解透彻操作系统呢?我们总是在操作系统中用着几个最常见的应用,qq、LOL、淘宝、360、优酷等等软件,然而我们却很少关注操作系统到底能干什么,假如没有操作系统会怎么样?
我们现在用的所有计算机结构都是由冯•诺依曼计算机演变过来的,下面我们简单看看冯诺依曼计算机结构:

我们可以看看,我们使用电脑基本就是用这几部分,有这几部分足够我们运行一个程序了啊!那么,我们需要操作系统干嘛呢?
首先我们要考虑到一点,由于运算器是能识别二进制码,所以我们在输入设备中只能输入很多很多的0和1组成的代码,这样的代码简直就是一股泥石流,让人绝望,然而早期的计算机还就是这样编程的,通过我们人工的方式写很多的0和1去对那些屏幕、cpu等硬件的驱动程序(比如声音驱动、显卡驱动,再驱动程序再底层其实就是用高低电位去使得硬件发生变化)发出指令进行操作;这个时候可没有什么线程什么的,没有cpu等待时间什么的,可想而知效率感人!
在操作系统没有出来的时候编程真的很痛苦,一般人真干不了!过了很多年之后,终于有了操作系统,操作系统本质上是一个软件,我们可以简单的把操作系统看作是对这些驱动的封装,在操作系统内部(可以叫做内核)提供了两类接口,一类是只要那些硬件驱动程序实现了这些接口,操作系统就通过这些接口使用那些硬件设备;然后另一类接口就是给我们程序员去实现的,我们只需要面向这些接口编程我们就可以间接的操作硬件!
而我们熟悉的JVM就是实现了操作系统提供的给程序员实现的那一类接口,而JVM又向JAVA程序员提供了一些java api,我们只需要按照java api就能间接的通过jvm对操作系统进行控制,进而对驱动发指令,最后就是可以对那些硬件中的数据进行处理;
于是我们可以看看下面这个图(暂时忽略UNIX命令和库),这个图中用户编写的应用程序可以看作是JVM,JVM可以根据操作系统提供的系统调用接口对操作系统内核发出一些指令,内核就会调用硬件的驱动程序对硬件进行一些操作,比如读取文件数据、向文件中写入数据等等

由于操作系统提供了这么强大的功能,于是我们现在几乎所有的编程语言都是在操作系统的基础上进行编程,然后通过解释器这类东西将我们写的程序转化为计算机能够识别的机器码,然后调用操作系统接口,最终实现对硬件的操作。
顺便一提,这里用户编写的应用程序我们就可以看作是QQ,优酷,酷狗音乐等等软件,如果是单核cpu,你同时打开多个应用,那么cpu会来回在多个应用切换,只是由于切换时间太短,我们感觉不到,还以为是同时运行的!就好像电影,其实电影是一张张图每隔零点几秒进行翻页,但是我们眼睛察觉不出来,还有我们鼠标移动在屏幕上移动,为什么这么流畅呢?因为我们的屏幕每隔零点几秒就刷新一次,当然我们察觉不出来。
现在我们看看操作系统的定义:是一组控制和管理计算机硬件和软件资源,合理对各类作业进行调度,以及方便用户的程序的集合”,是不是有点懂了,我们继续往下看!
2.操作系统的内存结构
我们可以简单看看操作系统的内存结构,一般查资料我们看到的是下图这样的,我们肯定看不懂!玛德,这都是什么鬼,除了那个栈和堆其他的都不知道干嘛的!

于是我们可以稍作修改,去掉一些不利于我们理解的东西,如下图所示,是不是就熟悉了一点了,貌似跟jvm的内存结构很像啊!!!

我们把硬盘和操作系统的PC寄存器添加上去试试效果,下图所示!有没有似曾相识的感觉,对的,jvm跟这个几乎一模一样,这里的硬盘其实就是相当于jvm的方法区(方法区也叫做永久代,看名字就知道是可以永久保存的啊!),还有jvm中的本地方法栈不在jvm的内存结构中,原来在这里啊,其实指的就是操作系统的栈;
操作系统的PC寄存器和jvm的PC寄存器用处差不多,是记录当前CPU运行命令的地址;

3.jvm在操作系统中的角色
举个简单的例子,当我们在电脑桌面上双击了QQ,那么QQ这个应用就会启动,操作系统就会创建一个进程,然后会在操作系统的堆中为这个进程开辟空间,用于存放该进程中产生的数据;
对操作系统来说,JVM和这个QQ应用没有什么两样,一视同仁,于是我们可以得到这样的一个图:

最后我们再把jvm中的内存结构完善一下,如下图:

看到这里,我们不禁的笑了,原来jvm就是按照操作系统的样子做了一遍,假如我们站在操作系统角度看,jvm其实就是一个平常的应用;假如我们站在java字节码文件的角度看,其实jvm就相当于一个操作系统,把字节码文件放进了jvm这个虚拟操作系统的硬盘中(方法区)中,然后jvm中对方法区中的数据进行读取、创建对象等后续各种操作;
操作系统栈和jvm栈基本一样;操作系统堆和jvm堆基本也一样,但是释放内存的方式有点差异,操作系统的堆是要程序员手动释放,而jvm的堆是靠gc自动清理;
现在我们再百度一下看看jvm的定义,现在应该知道jvm是个什么东西了吧!哈哈

4.方法区中的信息
虽然我们之前简要的说了方法区中的数据就是有关于类的基本信息,静态变量和常量池,但是说得比较笼统;
现在我们再回头看看jvm方法区中存的具体是什么信息:
(1)类信息
(2)字段信息
(3)方法信息
(4)常量池
(5)静态变量
(6)一个到Class对象的引用
(7)一个到加载该类的类加载器的引用
(8)方法表(有的JVM有,有的JVM没有):这是一个数组,保存了该类在堆中创建的所有对象的实例方法的引用,但是比较耗内存,所以有的jvm设计者没有设计这个方法表;
对应的java代码如下:
public final class com.wyq.test.ClassStruct extends Object implements Serializable {//1.类信息:包括访问修饰符,全类名,父类名,实现接口等
//2.对象字段信息:包括访问修饰符,类型,字段名
private String name;
private int id;
//4.常量池:常量和一些符号引用
public final int CONST_INT=0;
public final String CONST_STR="CONST_STR";
//5.静态变量
public static String static_str="static_str";
//3.方法信息:包括修饰符,方法返回值,方法名,局部变量,方法体(花括号中的内容)
public static final String getStatic_str (){
return ClassStruct.static_str;
}}
可以清楚的看到其实方法区中保存的就是将字节码文件中的所有信息!
5.java程序执行流程

现在我们结合操作系统的知识和上图看看我们运行一个java程序,电脑中到底是干了什么?就不考虑缓存了。。。
1.我们在Eclpse中写完一个java源程序,必须要Ctrl+S保存一下,这个动作就是将源程序保存到电脑硬盘中;
2.然后我们运行一个main方法,这个时候编译器就会将硬盘中的源程序读取到内存并编译成字节码文件(注意这个字节码文件不一定非要是本计算机编译的,只要是符合字节码文件格式的字节码文件都行,别人电脑传给你的肯定可以;这符合java一处编译到处运行的原则)
3.我们运行一个main方法的同时,对操作系统来说就是新创建了一个进程,就要在操作系统的堆中申请这个进程的内存空间,我们把这块内存空间称为JVM(注意,假如运行两个java程序那么这里就会创建两个jvm的内存空间)
4.jvm实例创建成功,就会在这个实例之中的内存空间进行分配,java栈,java堆,方法区等
5.由于类装载子系统,会把类加载器先加载到java堆中,然后类加载器根据我们的java源程序类名去指定路径中去加载字节码文件(双亲委托机制),放入方法区中
6.由执行引擎去执行这个字节码文件,伴随着验证、准备、解析和初始化,最终在java堆中生成了Class对象和实例化对象。
7.假如调用本地方法(就是Native修饰的方法)就会涉及到本地方法栈(和java栈作用差不多,压栈和弹栈),在操作系统栈中压入栈帧,假如在本地方法中还调用java中的方法,这个时候在操作系统的栈中压入一个栈帧,然后下一个栈帧却到了jvm的栈中,很有趣的一个东西。
8.我们方法调用完毕,栈中栈帧弹出,栈清理完毕;然后gc会对java堆中对象进行回收释放内存空间,然后gc还会对方法区进行清理,自此jvm中的内存空间清理完毕;
9 操作系统堆jvm进行清理,jvm进程结束。
总结:
由于对操作系统的理解还处于比较懵懂的状态,所以文中可能会有很多词语运用不当,很惭愧,以后肯定会抽个时间慢操作系统整个的学习一遍的,希望这一天不要来的太迟,哈哈哈
带着新人看java虚拟机03的更多相关文章
- 带着新人看java虚拟机02
上一节是把大概的流程给过了一遍,但是还有很多地方没有说到,后续的慢慢会涉及到,敬请期待! 这次我们说说垃圾收集器,又名gc,顾名思义,就是收集垃圾的容器,那什么是垃圾呢?在我们这里指的就是堆中那些没人 ...
- 带着新人看java虚拟机06(多线程篇)
其实多线程还有很多的东西要说,我们慢慢来,可能会有一些东西没说到,那就没办法了,只能说尽量吧! 1.synchronized关键字 说到多线程肯定离不开这个关键字,为什么呢?因为多线程之间虽然有各自的 ...
- 带着新人看java虚拟机04(多线程篇)
我记得最开始接触多进程,多线程这一块的时候我不是怎么理解,为什么要有多线程啊?多线程到底是个什么鬼啊?我一个程序好好的就可以运行为什么要用到多线程啊?反正我是十分费解,即使过了很长时间我还是不是很懂, ...
- 带着新人看java虚拟机01
1.前言(基于JDK1.7) 最近想把一些java基础的东西整理一下,但是又不知道从哪里开始!想了好久,还是从最基本的jvm开始吧!这一节就简单过一遍基础知识,后面慢慢深入... 水平有限,我自己也是 ...
- 带着新人看java虚拟机07(多线程篇)
这一篇说一下比较枯燥的东西,为什么说枯燥呢,因为我写这都感觉很无聊,无非就是几个阻塞线程的方法和唤醒线程的方法... 1.线程中断 首先我们说一说怎么使得一个正在运行中的线程进入阻塞状态,这也叫做线程 ...
- 带着新人看java虚拟机05(多线程篇)
上一篇我们主要是把一些基本概念给说了一下以及怎么简单的使用线程池,我们这一节就来看看线程池的实现: 1.线程池基本参数 以Executors.newFixedThreadPool()这种创建方式为例: ...
- Java虚拟机03(Java虚拟机内存模型)
根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 其实最需要Java程序员关注的是堆,栈,还有方法区,因为啊: 如果代码又问题的话,可能回出现栈溢出 然后说 ...
- java面试题——对于多态你是怎么理解的呢?不一样的角度,带你重新看java
java面试的时候经常会被问到一个问题,那就是java三大特性:继承,封装和多态.那么这三者的含义究竟是什么你真的清楚吗?我看网上大多都是人云亦云.所以我想把我的想法记录下来供大家参考-今天先聊一个, ...
- 深入理解Java虚拟机---学习感悟以及笔记
一.为什么要学习Java虚拟机? 这里我们使用举例来说明为什么要学习Java虚拟机,其实这个问题就和为什么要学习数据结构和算法是一个道理,工欲善其事,必先利其器.曾经的我经常害怕处理内存溢 ...
随机推荐
- Java c# 跨语言Json反序列化首字母大小写问题
C#标准是首字母大写,Java规范是首字母小写,在序列化成Json之后,反序列化会出现反序列化失败的问题.. 从C#反序列化成JavaBean的时候通过如下注解可以直接解决该问题 @JsonNamin ...
- flex 分页打印表格功能
private function printHandler():void{ var printJob:FlexPrintJob = new FlexPrintJob(); printJob.print ...
- Java开源生鲜电商平台-商品表的设计(源码可下载)
Java开源生鲜电商平台-商品表的设计(源码可下载) 任何一个电商,无论是B2C还是B2B的电商,商品表的设计关系到整个系统架构的核心. 1. 商品基本信息表:用单词:goods做为商品表 2. 商品 ...
- The more,the better。
贪婪是好的, 贪婪是对的, 贪婪是有用的, 贪婪是可以清理一切的, 贪婪是不断进化和进步的精华所在, 贪婪就是一切形式所在: 对于生活,对于爱情,对于知识我们一定要贪婪, 贪婪就是人们的动力. The ...
- C Primer Plus 第7章 C控制语句:分支和跳转 编程练习
作业练习 1. #include <stdio.h> int main(void) { char ch; int spare, other, n; //空格,其他字符,换行 spare = ...
- spring boot之从零开始开发自己的网站
概述 首先要感谢两位大神,该项目的想法来源自tale和MyBlog,本项目的想法. 做了一些改造,增加了一些功能和一些代码的重构,并且更换了博客主题. 关于项目,对于开发的练手项目,能够工程化,严谨一 ...
- Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主
前戏 在正式开始axios讲解前,让我们先想想,如何对现有的$.ajax进行简单的封装,就可以直接使用原声Promise了? let axios = function(config){ return ...
- Reflection的getCallerClass静态方法
Reflection的getCallerClass的使用 博客分类: java基础 Reflection的getCallerClass的使用:可以得到调用者的类.这个方法是很好用的. 0 和小于0 ...
- linux内核裁剪及编译可加载模块
一:linux内核裁剪: 1:编译内核源码: 今天的重点内容是内核驱动的编写,在编写驱动之前首先的了解linux内核源码,linux主要是由五个子系统组成:进程调度,内存管理,文件系统,网络接口以及进 ...
- repr调试python程序
一般调试程序的时候都比较倾向print,利用直接打印的方法作出判断,但是print只能打印出结果,对类型无法作出判断.例如: a = 5 b = ' print(a) print(b) 结果为: 5 ...