JAVA 运行机制小结

1、关系

Java运行包括4个方面Java代码(.java)Java编译文件(.class)Java虚拟机Java应用程序接口。Java代码通过编译器编译成为类文件,然后被装载到字节码内存中,通过类加载放入虚拟机中,最后通过操作系统和适配器实现,而JVM则处于核心地位。

2、Java虚拟机

Java虚拟机是java基础部分,Java语言具有跨平台的特性,这也是由JVM来实现的。更准确地说,是Sun利用JVM在不同平台上的实现帮我们把平台相关性的问题给解决了。Java语言支持通过JNI(Java Native Interface)来实现本地方法的调用,但是需要注意到,如果你在Java程序用调用了本地方法,那么你的程序就很可能不再具有跨平台性,即本地方法会破坏平台无关性。JVM包括类加载子系统、运行数据区、执行引擎和本地方法接口。

生命周期总结:当一个java程序启动时,JVM就产生一个实例;程序结束,实例也就消失了。Java虚拟机通常开始与一个main方法,这个方法是public static void修饰,JVM要调用必须是public,并且不通过对象调用,所以是static,而且由于JVM已经是底层,不会有任何返回,返回类型就成了void。

2.1类加载子系统

负责加载编译好的.class字节码文件,并装入内存,使JVM可以实例化或以其它方式使用加载后的类。JVM的类加载子系统支持在运行时的动态加载,动态加载的优点有很多,例如可以节省内存空间、灵活地从网络上加载类,动态加载的另一好处是可以通过命名空间的分隔来实现类的隔离,增强了整个系统的安全性。类加载可分为4类

启动加载器

启动加载器:BootStrap Class Loader负责加载JAVAHOME/lib下的jar包,比如rt.jar文件中所有的Java类,Java的核心类都是由该ClassLoader加载。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

扩展类加载器

扩展类加载器:Extension Class Loader负载加载JAVA_HOME/lib/ext下的jar包,可以由开发者直接引用。

系统类加载器

系统类加载器:(System Class Loader)负载加载ClassPath下jar包及目录,通常我们自己写的Java类也是由该Class Loader加载。在Sun JDK中,系统类加载器的名字叫App Class Loader

自定义类加载器

自定义类加载器:(User Defined Class Loader)由用户自定义类的加载规则,可以手动控制加载过程中的步骤。

加载:寻找并导入指定类型(类和接口)的二进制信息,将过类的全限定名和ClassLoader加载类,主要是将指定的.class文件加载至JVM。
当类被加载以后,在JVM内部就以类的全限定名+Class Loader实例ID来标明类。在内存中,Class Loader实例和类的实例都位于堆中,它们的类信息都位于方法区。加载中采用的是c,需要进行类加载时,先判断该类是不是已经进行加载,如果没有就在父类中进行加载,如果父类都找不到,就加载BootStrap class,如果这些都没有加载到,才加载自己的,这是Java安全性的一个保证。
加载阶段和连接阶段可能交叉进行,但是两个阶段建仍保持者固定的先手顺序

链接:将二进制的类型信息合并到JVM运行状态中。
第一步:验证,验证数据是否符合java类型,包括文件格式、元数据、字节码以及符号引用验证。
第二步:准备
第三步:解析

2.2运行时数据区

即内存空间,通常我们配置-Xms-Xmx信息都是设置的内存,Xms表示初始内存,Xmx表示最大内存,Xmn表示设置年轻代内存等等。内存空间主要由Java堆heap、方法区method area、本地方法栈、程序计数器、Java栈组成。其中Java堆、方法取每个线程公有,而本地方法栈、程序计数器、Java栈是线程私有。

程序计数器

一块较小的内存空间,是当前线程所执行的字节码的行号指示器。Java虚拟机的多线程是通过线程轮流切换执行,一个确定时刻,一个处理器确切说是一个内核,只会执行一条线程的指令,为了线程切换能够恢复正确位置,所以需要有一个独立的程序计数器。

Java虚拟机栈

同样为线程私有,就是java方法执行的内存模型,每个方法执行时都会同时创建一个栈帧,用户存储局部变量表、操作栈、动态链接、方法出口信息。每一个方法被调用直至完成过程,就是入栈到出栈的过程。

局部变量表存放了各种基本数据类型,对象引用类型(一个指向对象起始地址的引用指针,或者一个代表对象相关的位置),和returnAddress类型(指向一条字节码指令的地址)。局部变量表所需的内存空间在编译期间完成分配,并且空间已经固定,不会改变。

本地方法栈

本地方法栈与Java栈基本一样,只不过Java虚拟机栈是处理java方法(字节码)服务,而本地方法栈是为虚拟机使用Native方法服务。

后期学习:查看java字节码,使用 javap -verbose class文件;java -verbose文件名称 注此处没得后缀 是查看加载了哪些jar包和文件;javac -verbose java文件 是看虚拟器加载类哪些东西

Java堆 Heap

Java堆是虚拟机内存中最大的一块,Java对是被所有线程共享的一块内存区,用来存放对象实例。Java堆也是垃圾收集器管理的主要区域,由于现在垃圾收集器基本都是采用的分代收集算法,所以Java堆基本可以分为新生代老年代持久代,新生代再分为Eden空间、From survivor空间和To survivor空间。

新生代:用于存放新生的对象,对象在分配时首先分配到Eden区,当Eden区没有足够空间时,就会进行一次minor GC。通过-Xmn设置新生代大小,-XX:NewRatio=参数 设置新生代与老年代的内存空间比,-XX:SurvivorRation=参数 设置Eden区和Survivor比。

当Eden区进行minor GC后,如果对象经过一次回收并且还存活,能被Survivor去接收,就会移到Survivor(From)区,包括原From去中的对象,并将其年龄设置为1,每熬过一次minor GC年龄就会加1,当达到一定年龄后,就会晋升到老年代中。每次进行GC操作(Eden和From区),From区对象引入to区,并且和To区进行逻辑互换,保证一个Survivor区是空的,如果在放入To或者survivor区中内存不够时,会被放入Old区。Survivor设计成为两个区,应该回收中筛选更符合Old区条件的对象,因为Old区进行回收代价比较高

老年代:存放生命周期长的对象,或者是大对象(包括Eden区或者Survivor区无法放下的对象),当Old区被占满时就会进行完成的垃圾回收Full GC(Major GC)包括新生代。Full GC完成后,留下来的内存就会方法Permanent区(持久代)中。

方法区

方法区和Java堆一样,是各个线程共有的公共区域,用来存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。也可以被称作“永久代”,但两者本质上并不一样,只是把GC分代收集扩展到方法区了,相对Java堆而言,垃圾收集行为在访法区比较少见,主要针对常量池的回收和对类型的卸载。

运行时常量池

运行时常量池是属于方法区的一部分,是存放编译期生成的各种字面量和符号引用,当类被加载后,这些信息就会放入到访法区运行时常量池中。另外,运行区常量还具有动态特征,不要求常量一定要在编译期产生,运行期间也可能将新的常量放入常量池中,如String的intern方法。

直接内存

不是JVM中一部分,是由于new I/O 可以使用native直接分配堆外内存,然后通过存储在java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。

2.3 对象访问

对象访问在java中无处不在,即使最简单的访问,也会涉及到Java堆、Java栈和方法区。

如:Object obj = new Object()Object obj 作为对象引用,会被放入到Java本地栈中,通过一个reference获取,而引用方式有两种.

句柄式

先通过reference找到Java堆中的对象句柄地址,这个地址包含了对象实例数据(Java堆)和类型数据(方法区)的各自具体内存地址,通过这个地址去访问具体信息,这种方式的好处是如果reference存储的是稳定的句柄地址,在对象被移动时,只改变句柄中的地址指针,不用改变引用的地址。

直接式

就是reference引用时直接存储的对象地址,具体实现时reference指针找到对象实例(Java堆),而对象实例中又存储对象类型数据的地址,通过这个地址访问对象类型数据(方法区)。直接式的好处是节省了一对象定位的开销,使得访问速度更快。这两种具体取舍是看虚拟机如果实现的。

基本数据类型访问

基本数据类型由于字节类型大小都是固定的,所以需要的内存大小也都是固定的。基本类型左右全局变量时,基本数据类型声明是放在堆中的;而作为局部变量或者参数变量声明时,是存放在栈中的,方法结束后就释放。
对于String类型,字符串常量是放入字符串常量池,所以在方法区中,但是对于通过new String()出来的字符串,则在堆中。

3、性能优化

多线程性能

有序性、原子性、可见性

原子性:保证内存原子性操作read、load、assign、use、store、write这六个,volatile关键字不具有原子性,

可见性:一个线程修改共享变量值时,另外一个线程立即知道这个修改。由于java内存模式是通过变量修改后立即同步到内存,然后变量读取前从内存刷新变量值获取依赖关系。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容