深入JVM学习心得

深入JVM学习心得

前言

相信很多人和我一样长期使用java编程,却 很少关注过JVM底层实现,这很大程度上是因为JVM设计的很精巧,因此平时项目也很少遇到涉及JVM的问题。但是一方面出于对java底层技术的好奇, 另一方面某些高并发,要对特定场景优化或者是排错的问题也迫切需要对JVM实现的了解,于是楼主这两天仔细拜读了《inside JVM》这本关于JVM的经典著作,对JVM的一些实现细节有了较为清楚的认识,将一些学习的体会和收获记录下来与各位有相同困扰的朋友分享。

本文将从JVM的几大核心技术切入:JVM内存管理、class文件格式、类装载、垃圾收集、多线程并发。需要注意的是因为Java是一个平台无关的技 术,JVM在不同平台上必须有不同的实现,因此当年的Sun发布了一个JVM specification(Java 虚拟机规范)。任何团体或个人实现的JVM都必须遵照该规范才能正确的运行java程序。因此本文讨论的很多技术可能在不同的虚拟机上实现会有所不同,本 文只是讨论一些通用的技术以及虚拟机规范定义的一些要求。

JVM内存管理

在Java虚拟机规范中,将JVM虚拟机的内存分成了如下图中运行时数据区几大区域

这几个区域分别是方法区,堆,Java栈,PC寄存器,本地方法栈。接下来我们就来详细认识下这些内存区域的作用。

首先要说的是堆,堆中存放的是所有在java程序运行过程中创建的对象,因为在java里,数组是以对象的形式存在,因此数组也是存放在堆中的。堆占据了JVM的大部分内存。因此也是Java的GC,垃圾收集器主要工作的目标区域。

接下来要说的是方法区,方法区里存储了所有 类装载进来后和这个类相关的所有运行时需要的信息(如类的静态变量,常量,类的全局名称,方法信息等)。我们在后面介绍class文件的章节里会详细介绍 class文件加载进来之后是如何将这些信息对应写入方法区的数据结构中的。

和前面介绍的两个区域是所有线程共享的不同,Java栈和本地方法栈以及PC寄存器都是线程独占的,也就是说每个线程都有一个java栈和PC寄存器或者本地方法栈(如果用到了本地方法的话)。

说到这里需要介绍一下本地方法,我们知道 java是跨平台的,但是我们比如在需要读文件的时候,不用去关心将来是在哪个平台运行,只要调用FileInputStream把文件读入就可以了,不 用调用底层操作系统的API函数,这是因为不同平台Java的API把所有这些与平台相关的操作都封装了起来提供了一个统一的Java编程接口。而 Java的API正是通过调用一些本地方法(这些方法很多时候是一些编译后的可执行的C程序)来实现了这些功能。同时虽然Java实现了大部分平台都有的 一些功能(如IO,多线程等),但是有些平台的一些功能是该平台特有的,提供Java虚拟机的厂商为了提供这些功能往往就以动态链接库的形式提供一些本地 方法的调用来完善JVM在该平台的功能。至于如何去调用以及如何与本地方法通信(获取返回值等)就是具体JVM实现需要去做的事情。

说了这么多本地方法的内容,现在回到Java 栈的部分,每个线程都有一个自己独立的Java栈,每次线程执行到一个新的方法时就在栈里面压入一个栈帧。帧里包含了方法里的局部变量,操作数栈以及帧数 据区。这三种区域中局部变量很好理解,就是在方法作用范围内的变量,包括基本变量和对象的引用。理解操作数栈要先对JVM执行java程序的过程有所了 解,JVM在装载进class文件后可能采用解释执行、即时编译执行、混合执行这三种方式来执行class文件中的JVM指令集。JVM指令集是一个4字 节的指令集,就像汇编语言做相加操作需要先将两个数存入寄存器一样,JVM指令做数据相关的操作也要先将数据压入java栈里面的操作数栈才能进行。比如 方法里将i变量和j变量相加赋值给z,JVM先将i压入操作数栈,再将j压入操作数栈,最后将结果写回局部变量表或者是对象的字段。至于帧数据区,是为了 在方法执行过程中访问方法区的数据以及返回方法结果而用的。某个方法执行结束完之后如果是正常返回则会将返回结果压入上一个方法的操作数栈中,如果是异常 退出且没有catch该异常则会运行到上一个方法继续抛出该异常。

本地方法和Java方法一样,只是Java栈是执行Java方法的线程申请的内存,而本地方法是执行本地方法而申请的内存。下面这张图显示了两者的关系。

最后程序计数器是为每个线程记录当前执行的字节码位置而设立的,线程切换时需要记录下当前执行到哪一步了以便该线程重新获取CPU执行时能继续正确执行。

顺便说一句,在java里面对象是通过引用来 操作的,栈里面存储的引用,而堆里存储的对象。不同的JVM实现在引用的具体实现上可能有所不同,两种比较流行的方式分别是通过对象句柄引用和通过直接指 针引用。JVM的GC也是通过引用来确定哪些对象可以回收。下图分别表示了两种引用的实现:

对比这两种引用实现,句柄池的方法在GC需要 移动对象(消除内存碎片以存放大对象)时,只需要将句柄池中每个对象的指针地址修改即可。但是引用访问对象需要经过两个地址查找,降低了效率。直接指向对 象的方式在需要移动对象时要将每个引用的地址都做修改,这相对直接修改句柄池来说要昂贵的多,但是因为一次寻址提高了效率。

细心的读者可能注意到不管采用什么方式,每个 引用都有一个指向方法区里该类数据的指针。这是因为在java里面不像C++可以直接对内存对象做类型转换,Java类型转换前一定要做类型检查以保证这 次转换是安全的以避免可能因此带来的程序崩溃。因此每个引用都有一个指向类型数据的指针。

本文花了很大篇幅介绍java栈的内容,是因 为作者认为在这几个区域中,Java栈是最难理解的部分,希望读者能耐心读完,有什么问题也欢迎留言交流,最后为了加深对堆和栈存储哪些数据的理解,作者 写了两个分别产生OutOfMemoryError和StackOverflowError的函数以帮助理解,oom函数在数组对象s中不停的添加数据, 最后堆内存无法满足新的添加需求JVM就退出同时报出了OutOfMemoryError, stack()方法中有一个s的双精度局部变量,同时不停的递归调用自己,Java栈中就不停的压入新的方法栈,最后JVM退出并报出了 StackOverflowError

Java代码:

package Experiment;

import java.util.ArrayList;

public class TestJVM {
    public static void main(String[] args)
    {
        stackof();
        //oom();
    }
    private static void oom()
    {
        ArrayList<Integer> s=new ArrayList<>();
        while(true)
        {
        s.add(1);
        }
    }
    private static void stackof()
    {
        double s;
        stackof();
    }
}

运行结果:

原文地址:http://www.cnblogs.com/developerY/p/3330811.html 转载请注明出处

时间: 2024-10-20 19:03:16

深入JVM学习心得的相关文章

JVM学习心得

一.JAVA内存管理与GC机制 Java在JVM所虚拟出的内存环境中运行,java内存分为栈(stack)和堆(heap)两部分. 栈 在Java中,JVM中的栈记录了线程的方法调用.每个线程拥有一个栈,线程创建时创建栈.在某个线程的运行过程中,如果有新的方法调用,那么该线程对应的栈就会增加一个存储单元,即帧(frame).在frame中,保存有该方法调用的参数.局部变量.临时数据和返回地址. 栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中. 栈分为3个部分:基本

多线程及线程池学习心得

一.线程的应用与特点 多线程是程序员不可或缺的技术能力,多线程技术在各个方面都有应用,特别在性能优化上更是起到至关重要的作用.但是,如果多线程写得不好,往往会适得其反,特别是高并发时会造成阻塞.超时等现象.多线程具有以下特点:1.独立性,拥有自己独立的资源,拥有自己私有的地址空间:2.动态性,进程具有自己的生命周期和各种不同的状态:3.并发性,多个进程可以在单个处理器上并发执行,不会相互影响,并行是指同一时刻有多条指令在多个处理器上同时执行.线程是进程的组成部分,一个进程可以拥有多个线程,一个线

Linux系统理解以及Linux系统学习心得

原创作品转载请注明出处  <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 作者:严哲璟 说一下我对Linux系统的理解 1.加载Linux内核准备:在加载基本输入输出模块(BIOS)之后,从磁盘的引导扇区读入操作系统的代码文件块到内存中,之后开始整个系统的初始化. 2.main.c的start_kernel函数是整个操作系统的入口,这也与Linux是基于C语言的特性相符,start_kernel具体做的动作很多

我的MYSQL学习心得(八)

我的MYSQL学习心得(八) 我的MYSQL学习心得(一) 我的MYSQL学习心得(二) 我的MYSQL学习心得(三) 我的MYSQL学习心得(四) 我的MYSQL学习心得(五) 我的MYSQL学习心得(六) 我的MYSQL学习心得(七) 这一篇<我的MYSQL学习心得(七)>将会讲解MYSQL的插入.更新和删除语句 同样的,只会讲解跟SQLSERVER不同的地方 插入 将多行查询结果插入到表中 语法 INSERT INTO table_name1(column_list1) SELECT (

我的MYSQL学习心得(一)

我的MYSQL学习心得(一) 使用MYSQL有一段时间了,由于公司使用SQLSERVER和MYSQL,而且服务器数量和数据库数量都比较多 管理起来比较吃力,在学习MYSQL期间我一直跟SQLSERVER进行对比 第一期主要是学习MYSQL的基本语法,陆续还有第二.第三.第四期,大家敬请期待o(∩_∩)o 语法的差异 我这里主要说语法的不同 1.默认约束 区别:mysql里面DEFAULT关键字后面是不用加括号的 --sqlserver CREATE TABLE emp ( id INT DEFA

JVM学习(4)——全面总结Java的GC算法和回收机制---转载自http://www.cnblogs.com/kubixuesheng/p/5208647.html

俗话说,自己写的代码,6个月后也是别人的代码--复习!复习!复习!涉及到的知识点总结如下: 一些JVM的跟踪参数的设置 Java堆的分配参数 -Xmx 和 –Xms 应该保持一个什么关系,可以让系统的性能尽可能的好呢?是不是虚拟机内存越大越好? Java 7之前和Java 8的堆内存结构 Java栈的分配参数 GC算法思想介绍 –GC ROOT可达性算法 –标记清除 –标记压缩 –复制算法 可触及性含义和在Java中的体现 finalize方法理解 Java的强引用,软引用,弱引用,虚引用 GC

在马哥linux运维学院学习心得

题目:在马哥linux运维学院学习心得 姓名:谭龙 班级:M18 学号:26 时间:2016-02-29--2016-06-02(正常毕业时间预计在7月中上旬)   正文: 个人基本情况: 我是一名在校的即将毕业的大四学生,毕业时间为2016.7.专业为矿物加工工程专业,纯正的四川-广安人(邓小平故居就在那).因找不到工作,加上自己也不知道干什么,在堂弟的推荐下,来参加了马哥linux运维学院的学习:怀揣着一颗对计算机懵懂的心,开始涉足从未接触过了linux. 个人收获与心理变化: 在一开始接触

第一篇大数据学习心得

之前未习惯发布学习心德博文,后续会采用这种方式发布学习心得,希望能够很好的督促自己. 计划会按scala,Hadoop,Spark的顺序去学习. 刚学scala的时候,眼前一亮,这语法跟python,java很像啊,刚好两者很熟悉,偷笑,后面果然学的得心应手.今天就不发表具体的技术内容.反正王学林老师的视屏讲解很好,声音非常富有感染力,想开小差都比较难,呵呵,话语精炼,个人较喜欢的风格,这里说下这段时间学习scala的小心得?,视频学完一章紧接着进行敲代码,调试,最后记笔记,对,记笔记,不一定是

spring核心知识(学习心得)

直接进入主题,主要分为两大部分:框架学习心得和spring框架的核心知识. 学习心得 1.学习框架的时候,一定要弄清楚的几个问题: a. 这是一个什么框架 轻量级还是重量级, 侵入式还是非侵入式,是解决单个问题还是整体的解决方案. b. 框架的设计理念是什么(为了解决什么问题而出现) c. 框架的优缺点 d. 框架的架构是怎样的 e. 框架的核心是什么 f. 框架能实现哪些功能 在学习一个框架的时候如果都不知道它能够提供哪些功能,就更加不用谈功能实现和充分利用框架了 2. 在学习多个框架以后,如