一、堆

定义: Heap,通过new关键字创建的对象,都存放在堆内存中。

特点

  • 线程共享,堆中的对象都存在线程安全的问题
  • 垃圾回收,垃圾回收机制重点区域。

jvm内存的划分:

  • JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。
  • 年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
  • 非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。
  • 年轻代(New):年轻代用来存放JVM刚分配的Java对象
  • 年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代
  • 永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间,方法区。

堆内存查看的相关指令:

  • jps

  查看系统有哪些进程。

  • jmap

  查看堆内存使用情况 jmap -heap PID

  • jconsole

  图形界面,多功能检测工具,连续监测

二、方法区

定义: 其中主要存储class文件的信息和运行时常量池,class文件的信息包括类信息和class文件常量池。

class文件结构:

  • 最头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受
  • 接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号
  • 再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值、类信息、父类与接口数组、方法信息。

三、常量池、运行时常量池、字符串池

1、常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息,我们可以通过Javap -v  类名.class 指令反编译一个简单的程序看到如下的常量池信息

左边“#1”为常量池中的符号地址。

2、运行时常量池:常量池是 class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址。

3、字符串池:在JVM里实现字符串池功能的是一个StringTable类,它的底层是一个HashTable,里面存的是字符串对象的引用(而不是字符串实例本身),真正的字符串实例是存放在堆内存中的(并且字符串池在逻辑上是属于运行时常量池的一部分)

4.常量池和字符串池的关系:

下面来看段代码:

public static void main(String[] args) {
String s1 = "b";
String s2 = "c";
String str = new String("b");
System.out.println(s1 == str); //false
}

然后通过反编译观察字节码文件

说明:在jdk1.8时,最开始编译时字符串都是常量池中的符号,尚未转化为对象,当程序执行时,常量池中的信息都会被加载到运行时常量池中,这才转化成了对象,并且看StringTable中有没有"b","c"对象,如果没有则把 "b" 和 "c" 对象的引用值存入StringTable,真正的对象实例则在堆中;如果有的话则不会存入,这样就避免了重复创建字符串对象。

再来分析String str = new String("a")这行代码:

可以看出,这行代码创建的对象个数因StringTable中有没有“b”对象而异,如果字符串池有“b”,则此时只会创建一个对象:也就是new的一个字符串对象,存放在堆中;如果没有就会创建两个对象,一个是new的对象存放在堆中,一个是“b”字符串常量对象,存放在StringTable中。

下面我们再看一个例子:

public class HelloWorld {
public static void main(String []args) {
String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);//true
System.out.println(str2 == str4);//false
System.out.println(str4 == str5);//true
}
}

看到String str3 = "abc"; 解析str3时,在StringTable中寻找“abc”,会发现str1的值已经在stringTable中,所以str3的引用地址和str1相同,不会创建不同的对象,即str1==str3为true;

看到String str4 = str2.intern();我们可以知道,intern()函数返回StringTable中”def”的引用值。因为StringTable中已经有“def”引用值,即返回str2中new出来的“def”在StringTable中的引用值。

StringTable 的位置

jdk6(永久代实现)和jdk8(元空间实现)中方法区的区别,其中最主要的区别是将方法区转移到本地内存中,且常量池分为运行时常量池和字符串常量池;且字符串常量池被留在内存中的堆中。

原因:

  • StringTable中存在大量的字符串对象,运行时间增长永久代内存占用过多,且永久代只有在触发FULL GC时才进行垃圾回收,回收频率过慢。
  • 转移到堆中可以利用虚拟机在堆内存中频繁的垃圾回收,处理StringTable中对象过多情况。

永久代和元空间内存溢出的区别:

  • jdk1.6
  • jdk1.8

jdk1.8和jdk1.6中intern()方法的运用

  • 1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则将该字符串的引用放入串池
  • 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池

总结

  1. 全局字符串池每个虚拟机只有一个,存储字符串常量的引用值;
  2. class常量池是java程序编译之后才有的,每个类都有,存放字面值和符号引用常量;
  3. 运行时常量池是在类加载完之后,常量池内容存储在运行时常量池中,每个类都有一个,且常量池中符号引用转换为直接引用,与全局字符串池中保持一致。

StringTable调优:

  • 调整hash表中桶子个数,-XX:StringTableSize=桶个数
  • 考虑字符串是否入池

四、直接内存

  • 常见于NIO操作中,用于数据缓冲
  • 分配回收成本高,但读写能力强
  • 不受JVM内存回收管理

直接内存使用前后的对比:

使用前:

说明:

  • 因为java无法操作本地文件,在java堆内存中划出java缓冲区;
  • 从用户态转移到内核态,本地方法在系统内存中划出一段系统缓冲区,将磁盘文件分部分缓冲到系统缓冲区中,间接的将系统缓冲区中数据传输到java缓冲区中;
  • 内核态转到用户态,调用输出流写入操作,将文件copy到另一个位置,循环copy,直到全部复制完成。

使用后:

说明:

  • ByteBuffer.allocateDirect(_size),在系统内存中分配直接内存;
  • 系统方法和java方法都可以访问直接内存;
  • 与不使用直接内存相比,减少了一次从系统缓存区向java缓冲区复制的操作,复制效率成倍上升。

直接内存的回收:

  • 使用Unsafe对象实现直接内存的分配回收,回收主要使用的是freeMemory方法
  • ByteBuffer类内部,使用了Cleaner(虚引用)来检测ByteBuffer对象,一旦对象被回收,就会由ReferenceHandler线程通过Cleaner的clean对象调用freeMenory来释放直接内存。
  • -XX:+DisableExplicitGC 显式的System.gc()显式的垃圾回收 FULL GC,被禁用。
  • 因为考虑到系统性能,FULL GC时间够长,会严重影响性能。所以涉及到直接内存的使用,释放内存使用Unsafe.freeMemory,不建议使用System.gc()。

jvm入门及理解(四)——运行时数据区(堆+方法区)的更多相关文章

  1. JVM学习笔记:Java运行时数据区域

    JVM执行Java程序的过程中,会使用到各种数据区域,这些区域有各自的用途.创建和销毁时间.根据<Java虚拟机规范>,JVM包括下列几个运行时数据区域,如下图所示: 其中红色部分是线程私 ...

  2. jvm系列(一)运行时数据区

    C++程序员肩负着每一个对象生命周期开始到终结的维护责任.Java程序员则可以借助自动内存管理机制,不需要自己手动去释放内存.由虚拟机进行内存管理,不容易出现内存泄漏和内存溢出的问题,但是一旦出现这些 ...

  3. JVM笔记【1】-- 运行时数据区

    目录 (一)java内存区域管理 1.1 程序计数器 1.2 虚拟机栈 1.3 本地方法栈 1.4 java堆 1.5 方法区 1.5.1 运行时常量池 (二)直接内存 (一)java内存区域管理 C ...

  4. JVM之基础概念(运行时数据区域、TLAB、逃逸分析、分层编译)

    运行时数据区域 JDK8 之前的内存布局 JDK8 之后的 JVM 内存布局 JDK8 之前,Hotspot 中方法区的实现是永久代(Perm),JDK8 开始使用元空间(Metaspace),以前永 ...

  5. 深入浅出JVM(一):运行时数据区域

    程序计数器 线程私有 指向了正在执行的虚拟机字节码指令的地址:如果是本地方法,数值为空 没有 OutOfMemoryError 错误的区域 Java虚拟机栈 线程私有: 生命周期与线程相同: 代表着 ...

  6. JVM 专题十二:运行时数据区(七)对象的实例化内存布局与访问定位

    1. 对象的实例化 1.1 创建对象的方式 new 最常见的方式 变形1 : Xxx的静态方法 变形2 : XxBuilder/XxoxFactory的静态方法 Class的newInstance() ...

  7. 深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域

    深入理解Java虚拟机 -- 读书笔记:JVM运行时数据区域 本文转载:http://blog.csdn.net/jubincn/article/details/8607790 本系列为<深入理 ...

  8. JVM详解(四)——运行时数据区-堆

    一.堆 1.介绍 Java运行程序对应一个进程,一个进程就对应一个JVM实例.一个JVM实例就有一个运行时数据区(Runtime),Runtime里面,就只有一个堆,一个方法区.这里也阐述了,方法区和 ...

  9. 深入理解JVM(2)——运行时数据区

    1.运行时数据区 1.1.程序计数器 记录当前线程正在执行的字节码指令的地址,如果正在执行的是 Native 方法,这个计数器值则为空. 1.2.虚拟机栈 每个 Java 方法在执行的同时会创建一个栈 ...

  10. Java JVM运行时数据区,内存管理和GC垃圾回收

    一 . 运行时数据区 程序计数器是线程私有的,是一块很小的内存空间,是当前线程执行到字节码行号的计数指示器.每个CPU处理器核心 在任何一个时刻,都只可能运行着唯一的一个线程,执行着一条指令.所以在多 ...

随机推荐

  1. JVM 常用参数一览表(转)

    参数 默认值或限制 说明 参数 默认值 功能 -XX:-AllowUserSignalHandlers 限于Linux和Solaris,默认不启用 允许为java进程安装信号处理器,信号处理参见类:s ...

  2. 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!

    罗曼罗兰说过:世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手 ...

  3. 性能测试工具Jmeter你所不知道的内幕

    谈到性能测试,大家一定会联想到Jmeter和LoadRunner,这两款工具目前在国内使用的相当广泛,主要原因是Jmeter是开源免费,LoadRunner 11在现网中存在破解版本.商用型性能测试工 ...

  4. FtpServer穿透内网访问配置踩笔记

    FtpServer穿透内网访问配置踩笔记 引言 FtpServer是服务器文件远程管理常用方式. 以前在局域网配置Ftp服务器以及使用公网上的Ftp服务均未碰到问题,固未对Ftp传输进行深入了解. 然 ...

  5. 使用PostgreSQL注意事项

    一.大小写特别敏感 大写字段需要用“”引号(pg字段名使用“”,MySQL字段名使用``) ******表名以及字段名如果是小写但是为关键字,比如name,则也需使用"": 二.分 ...

  6. Ubuntu16.04下安装搜狗输入法及实现中英文转换问题

    1.问题描述 版本信息:Ubuntu16.04 解决问题:搜狗输入法的安装 2.解决办法 STEP1:搜索搜狗输入法for Linux --> 选择64bit --> 下载得到一个sogo ...

  7. Selenium系列(十四) - Web UI 自动化基础实战(1)

    如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...

  8. Mongodb中 数据库和集合的创建与删除

    1.查询数据库,查询表: show dbs //查询所有的数据库show collections //查询所有的集合(表) 2.创建数据库或切换到数据库(存在就切换,不存在就创建) use spide ...

  9. 蓝桥杯 K好数(Java)

    越来越觉得自己菜,一道简单的动态规划写不出来,题解也是看了很多份才看懂了,所以尽量以图表的方式写了题解,希望我的题解能帮到其他人吧.(;´Д`) 首先是题目: 输入描述: 输入包含两个正整数,K和L. ...

  10. sql 数据库操作语句 不带select

    MySQL数据操作语句 1.总纲 DDL -数据定义语句** create/drop/alter ** create: 创建 drop:删除 alter:修改 DML -数据操作语句 ** inser ...