Java内存区域划分

1、程序计数器
线程私有,当前线程执行的行号指示器,指向当前线程执行的虚拟机字节码地址,线程的恢复,跳转等都需要用到它 2、Java虚拟机栈
线程私有,虚拟机栈描述的是Java内存模型,用于存储局部变量、操作数栈、动态连接、方法出口等信息;Java每一个方法的执行都对应一个栈帧的出栈和入栈的过程
可通过-Xss参数来设定栈容量 3、本地方法栈
线程私有,类似于Java虚拟机栈,用于执行虚拟机用到的native方法,与Java虚拟机栈原理一致,只是它们服务的对象不一样 4、Java堆
线程共享,Java堆在虚拟机启动的时候被创建,此区域的唯一目的就是存放对象,Java虚拟机规范规定:所有的对象实例和数组都要在堆内存分配
为了方便内存的回收,将Java堆分为老年代和新生代,方便采用分代收集算法回收内存
虚拟机启动时,可通过-Xmx(最大值)和-Xms(最小值)两个变量来控制堆的大小 5、方法区
线程共享,用于存放被虚拟机加载的类信息、常量、静态变量、即时编译生成的代码等。
该区域的垃圾回收主要针对常量池的回收和类型的卸载,但总是令人难以满意,因为类的卸载条件非常苛刻
通过-XX:PermSize和-XX:MaxPermSize来设置方法区的容量 6、运行常量池
方法区的一部分,主要用于存放编译期生成的各种字面量和符号引用 7、直接内存
直接内存不属于虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,直接内存不会收到Java堆大小的限制
直接内存的作用是:使得Java可以通过DirectByteBuffer对象直接操作这块内存,避免Java堆和Native堆中来回的数据复制,从而提高性能
可通过-XX:MaxDirectMemorySize来指定直接内存的大小

对象创建过程

1、虚拟机遇到一条new指令时,先去检查这个指令的参数能否在常量池中找到对应类的符号引用,并检查这个类的符号引用是否被加载、解析、和初始化过,如果没有,就执行加载和初始化过程
2、在类加载检查通过后,虚拟机将为新生对象分配内存。
分配内存有两种方法:一、指针碰撞。如果内存中已经使用的内存放在一边,未使用过的内存放在一边,中间用一个指针作为分界点的指示器,则将指针向未使用内存一方偏移对象大小的距离,称为指针碰撞。二、空闲列表法。如果内存不连续,则虚拟机中会维护一个空闲空间内存地址的列表,虚拟机会在这个列表中找到一块足够大的内存分配给新的变量,称为空闲列表法。
那如何保证内存分配的安全性呢?一、对内存空间的分配动作采用同步处理。事实上,虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。二、对每个线程,在Java堆中预先分配一小块内存,称为本地线程分配缓冲TLAB,以此来隔离各个线程的内存分配,使得它们互不干扰 3、内存分配完成后,虚拟机将分配到的内存空间都初始化为零
4、对对象进行必要的设置。如:类的元数据、对象的哈希码、对象的GC分代信息等
5、调用对象的init方法,执行初始化,完成对象创建

对象的内存布局

1、在HotSpot虚拟机中,对象分内存分为:对象头、实例数据和对齐填充三个部分

对象头包含两个部分,第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、偏向线程ID、偏向时间戳等;
第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过该指针确定对象是哪个类的实例 实例数据用于存储类真正的有效信息,即程序代码中定义的各种类型的字段内容 对齐填充,这部分并不是必然存在,也没有特别意义,仅仅是起着占位符的作用

对象访问

Java程序是通过栈上的reference数据来操作具体对象的。目前主要的对象访问方式有两种:
一、句柄访问。reference中存储的是对象的句柄地址,而句柄中包含了对象实例数据、类型数据各自的具体地址信息
二、直接访问。reference中直接存储对象的地址

如何判断对象是否存活

1、引用计数法:给对象添加一个引用计数器,每当一个地方引用它时,计数器加1;当引用失效时,计数器减1,当计数器为0,表示该对象不再被使用,应该回收。
优点是:实现简单,判断效率高。缺点是:很难解决对象之间互相循环引用的问题。例如objA.instance=objB;objB.instance=objA;其他地方再无引用这两个变量,但是因为它们互相引用着对方,因而计数器都不为零,导致GC无法回收它们
实际上,Java的大多虚拟机实现都没有采用上面这种方法 2、可达性分析法:通过一系列称为GC Roots的对象作为起点,从这些起点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链,证明该对象不可用,应该被回收

Java中的引用

引用:如果reference类型的数据中存储的数值代表另一块内存中的起始地址,就称这块内存代表着一个引用
引用分为强引用、软引用、弱引用、虚引用四种
强引用:如果对象存在强引用,就不能被垃圾收集器回收,入Object obj = new Object();
软引用:用来描述一些还有用但并非必须的对象。在系统发生内存溢出之前会将这些对象回收
弱引用:用来描述一些非必须的对象,比软引用更弱一些。被弱引用关联的对象只能存活到下一次垃圾回收之前
虚引用:也称为幽灵引用,是最弱的一种引用关系。一个对象有虚引用,完全不会对其生命周期产生任何影响,也不能通过虚引用来创建对象实例,为对象设置虚引用的目的是在对象被垃圾回收之前获得一个通知

垃圾回收算法

为什么要了解垃圾回收?当要排查各种内存泄漏、内存溢出时,当垃圾回收变成系统高并发的瓶颈时,我们需要对这些自动化技术进行监控
1、标记-清除算法
基本思想:首先标记出需要回收的对象,然后在标记完成之后统一回收被标记的对象。
不足之处:一、标记和清除两个过程效率都不高;二、空间问题,标记清除后会产生大量不连续的空间,形成空间碎片,导致后面的大对象可能无法找到一个足够大的连续内存,从而触发提前GC 2、复制算法
基本思想:将内存分为大小相等的2块,当一块内存用完,就将还存活的对象复制到另一块内存,再把使用过的内存一次全部清理掉
优点:不存在内存碎片;内存分配时只需要移动指针即可,实现简单,运行高效
缺点:内存浪费,内存空间只有一半的利用率;在对象存活率高时,复制效率将会比较低
主要用于新生代,因为新生代中,正常80%的对象都是朝生夕死的,所以可以采用8:1:1的比例来划分内存,用两个较小的内存来存放第一次回收时没有被回收掉的对象 3、标记-整理算法
基本思想:类似于标记-清理算法;先将可回收的对象标记出来,然后将所有存活的对象都复制到另外一边,之后清理掉另一边的所有内存 4、分代收集算法
基本思想:根据对象存活周期的不同将内存划分为几块,然后对不同的区域采用不同的收集算法。
通常将内存划分为新生代和老年代。新生代中,每次垃圾回收时都会有大量的对象死去,可选用复制算法,每次都可以用少量的复制成本完成收集;对于老年代,对象存活率高,可采用标记-清理算法或则标记-整理算法

HotSpot的关键概念

1、枚举根节点
在HotSpot中,使用了一种叫做OopMap的数据结构来实现准确实GC,完成GC Root 枚举 2、安全点
在进行GC时会挂起所有线程,正在执行的线程挂起后,GC完成后可正确恢复的点 3、安全区域
线程执行到该区域中的任务位置开始GC而被挂起后,线程都可正确恢复的区域,被称为安全区

常见的收集器

1、Serial收集器。单线程,常用于新生代,特点,执行垃圾回收时挂起所有其他线程
2、ParNew收集器。Serial的多线程版本
3、CMS收集器。

关于垃圾回收

对象被创建时分配到eden区,如果eden区没有足够的内存,则触发一次MinorGC,如果还是无法获取足够的内存分配对象,则会触发Full GC,通常FullGC的速度会比Minor GC的速度慢上很多
走过一次Minor GC,对象的年龄增加1岁,默认超过15岁的对象会被移动到老年代,可以通过设置-XX:MaxTenuringThreshold来设置阈值,
对于大对象,可以设置-XX:pretenureSizeThreshold参数来规定大于某个值得大对象直接分配到老年代,来避免Eden区和Survivor区之间频繁的大量内存复制
PretenureSizeThreshold这个参数只对Serial和ParNew这两个收集器有效 空间分配担保:就是垃圾手机器为了避免频繁的Full GC,在进行一次Minor GC时,老年代会为新生代做一次担保,保证如果回收后新生代仍不满足内存需求的情况下将一部分新生代的对象移动到老年代,
避免频繁GC

常用的JDK命令行工具

jps Java虚拟机进程状态工具
显示指定系统内所有的Hotspot虚拟机进程,有四个可选参数:
-q 只输出虚拟机唯一主机id(LVMID),省略主类名称
-m 输出虚拟机启动时传递给主类main函数的参数
-l 输出主类的全名,如果启动时是jar包,输出jar包路径
-v 输出虚拟机启动时JVM参数 jstat 虚拟机统计信息监控工具
用于收集Hotspot虚拟机各方面的运行数据 jinfo Java配置信息工具
显示虚拟机配置信息
实时查看和调整虚拟机的各项参数 jmap Java内存映像工具
生成虚拟机内存快照 jhat 虚拟机转储快照分析工具用于分析heapdump文件
用于分析heapdump文件 jstack Java堆栈跟踪工具
显示虚拟机当前时刻的线程快照
线程快照就是当前虚拟机内每一条线程正在执行方法堆栈的集合,生成快照的目的是定位线程出现长时间停顿的原因
线程停顿的时候可以通过jstack来查看各线程的调用堆栈
有以下几个可选参数:
-F 当前正常输出的请求不被响应,强制输出线程堆栈
-l 除堆栈外,显示关于锁的附加信息
-m 如果调用到本地方法,可以显示C/C++的堆栈

类的加载机制

类的加载时指虚拟机把描述类的class文件加载到虚拟机内存,并对数据进行校验、转换解析和初始化,最终形成虚拟机可以直接使用的Java类型的过程

Java虚拟机笔记的更多相关文章

  1. Java虚拟机笔记(五):JVM中对象的分代

    为什么要分代 为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我们要找到哪些对象没用, ...

  2. Java虚拟机笔记(四):垃圾收集器

    前言 前一篇文章介绍了内存的垃圾收集算法,现在介绍下内存回收的具体实现--垃圾收集器. 由于Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商,不同版本的虚拟机所提供的垃圾收集 ...

  3. Java虚拟机笔记(三):垃圾收集算法

    一.标记-清除(Mark-Sweep)算法 标记清除算法是最基础的收集算法,其他收集算法都是基于这种思想. 标记清除算法分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一清除对 ...

  4. Java虚拟机笔记(二):GC垃圾回收和对象的引用

    为什么要了解GC 我们都知道Java开发者在开发过程中是不需要关心对象的回收的,因为Java虚拟机的原因,它会自动回收那些失效的垃圾对象.那我们为什么还要去了解GC和内存分配呢? 答案很简单:当我们需 ...

  5. Java虚拟机笔记(一):类加载机制

    一.概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 二.类加载的生命周期 类从被加载到 ...

  6. java虚拟机笔记-1

    java虚拟机学习笔记 Java技术的核心就是Java虚拟机,因为所有的Java程序都在虚拟机上运行.Java程序的运行需要Java虚拟机.Java API和Java Class文件的配合.Java虚 ...

  7. 深入理解java虚拟机笔记Chapter12

    (本节笔记的线程收录在线程/并发相关的笔记中,未在此处提及) Java内存模型 Java 内存模型主要由以下三部分构成:1 个主内存.n 个线程.n 个工作内存(与线程一一对应) 主内存与工作内存 J ...

  8. Java虚拟机笔记 – JVM 自定义的类加载器的实现和使用2

    1.用户自定义的类加载器: 要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名 ...

  9. 深入理解Java虚拟机笔记

    1. Java虚拟机所管理的内存 2. 对象创建过程 3. GC收集 4. HotSpot算法的实现 5. 垃圾收集器 6. 对象分配内存与回收细节 7. 类文件结构 8. 虚拟机类加载机制 9.类加 ...

  10. 深入理解java虚拟机笔记之一

    Java的技术体系主要有支撑java程序运行的虚拟机,提供各开发领域接口支持Java API,java编程语言及许多第三方java框架( 如Spring,Structs等)构成. 可以把Java程序设 ...

随机推荐

  1. Python-爬虫-HTTP协议请求之GET请求

    我们在百度搜索时,输入关键词,比如“hello”,URL发生变化,如下: https://www.baidu.com/s?wd=hello&rsv_spt=1&rsv_iqid=0xf ...

  2. struts漏洞处理--老项目struts版本升级遇到的问题

    struts漏洞S2-016被扫描出,要求升级struts版本,查看生产struts版本2.0.12,该漏洞影响版本2.3.15以下,上网搜索,struts2.5以上的要求jdk1.7,由于项目过老, ...

  3. SSDT

    2.系统服务调度表SSDT及SSSDT Shadow 系统服务:由操作系统提供的一组函数(内核函数),API可以间接或者直接的调用系统服务.操作系统以动态链接库(DLL)的形式提供API. SSDT: ...

  4. 数据结构C++版-图

    一.概念及分类 二.图的存储结构 1.邻接矩阵 顶点: 弧: 边: 表达式语句: 2.邻接表 逆邻接表: 3.十字链表 4.邻接多重表 三.图的权值概念及遍历 权值: 图的遍历: 1.深度优先搜索 2 ...

  5. 剑指offer——10跳台阶演变

    题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳法.   题解: 纯找规律题:   class Solution { public: ...

  6. 网页重构应该避免的10大CSS糟糕用法

    对于网页重构来说,CSS禅意花园 是网页布局从 table 表格转到了 html +css 的标志 .这些年来,随着我们的网站越来越复杂:html5,css3,新的技术.新的属性,越来越多的开发者开始 ...

  7. fastReport.net 初了解

    delphi 中fastReport rmReport都很好用,转到.net了,第一想法也是这两个,好在这里有个fastReport; 这个安装呢 找个破解的 有个4.x版 安完建一个winForm  ...

  8. C++数据类型之字符型&转义字符

    字符型 **作用:** 字符型变量用于显示单个字符 **语法:**  char ch = 'a'; > 注意1:在显示字符型变量时,用单引号将字符括起来,不要用双引号 > 注意2:单引号内 ...

  9. flink widow&window funcion&水印

    在定义了窗口分配器之后,我们需要为每一个窗口明确的指定计算逻辑,这个就是窗口函数要做的事情, 当系统决定一个窗口已经准备好执行之后,这个窗口函数将被用来处理窗口中的每一个元素(可能是 分组的). 谁可 ...

  10. Spark中的各种action算子操作(java版)

    在我看来,Spark编程中的action算子的作用就像一个触发器,用来触发之前的transformation算子.transformation操作具有懒加载的特性,你定义完操作之后并不会立即加载,只有 ...