jvm相关自我总结和 VisualVM工具的使用
idea 二个工具: jclasslib Hexview
jdk监控工具 VisualVM工具的使用: https://www.ibm.com/developerworks/cn/java/j-lo-visualvm/index.html
对象的创建过程?:
new--> 申请存储空间, 创建对象,此时对象处于半初始化状态
invokespecial #1 ---> 调用实例初始化方法,私有方法,父类构造方法
astore_1 ---> 将对象和栈中的局部变量建立联系
return -----> main函数跳出
DCL和volatile的问题, volatile不能少, 因为对象创建这个过程有可能出现指令重排, new--> invokespecial --> astore_1 这个过程 有可能会优化成 new --> astore_1 ---.invokespecial
这样会导致第一次判断实例是否为null, 此时对象处于半初始化状态,不为null, 从而返回, 破坏了单例模式
synchronized的底层原理:
java对象加了一个 synchronized 关键字
编译成字节码, 通过反编译可以看到 加了一个监视器 (monitorenter, monitorexit),
在jvm执行的过程中进行 锁升级,偏向锁--> 自旋锁 --->重量级锁
更底层的实现是: 汇编指令 lock comxchg
对象在内存中的存储布局: markword(8字节) class pointer(类型指针4字节,用于判断对象属于哪个类的实例), instance data(实例数据), padding(对其补充)
markword, class pointer 是对象头,对象头部markword的信息有:存储对象自身运行时数据, 锁信息, 对象分代年龄, hashcode,线程持有的锁, 偏向线程ID, 偏向时间戳
一个对象new出来, 此时是没有什么锁的, 当被 synchronized修饰,且只有一个线程来访问对象时候,此时, 会将这个线程id 标记到这个对象头(这就是偏向锁),这个线程一看是自己的线程id,就开始使用这个对象,
当有多个线程都来争抢这个对象时候,此时锁升级,升级为 自旋锁, 每个线程会在本线程内生成一个 lock record, 并都尝试将 这个lock record 添加到这个对象的markword中, 添加成功的线程开始使用这个对象, 添加失败的线程会在这里自旋(CAS) , 当这里自旋的线程变得很多的时候,会很消耗资源, jdk6之前是有个调优设置: 自旋的线程超过cpu核数一半,或者自旋次数超过10次, 就开始将自旋锁升级为 重量级锁, 现在的jdk8和之后版本中是有个 自适应机制,jvm自己来管理什么时候升级锁
重量级锁: 这里牵扯到二个概念: 用户态,内核态, 操作系统需要二种CPU状态, 内核态: 运行操作系统程序,操作硬件, 用户态: 运行用户程序, jvm执行代码,偏行锁和自旋锁处于用户态时候, 当锁升级为内核态时候, 向内核申请重量级锁, 重量级锁将所有自旋的线程放到一个队列中,排队执行,, 避免cpu空转
锁消除: 代码在JIT即时编译时,通过对上下文进行分析,去除掉没必要的加锁请求,比如一个方法中,new StringBuffer,之后不停的append, 由于这个StringBuffer对象只是在这个方法中使用,且append方法被synchronized修饰,如果不停的加锁,解锁,会消耗cpu资源, 所以jvm会将这里的锁消除
锁粗化: 代码在JIT即使编译时, 通过上下文分析,将多个锁合并为同一把锁,这样避免频繁加锁,解锁,而是共用同一把大锁, 比如一个方法中,new一个 stringBuffer while(true)代码块中,对这个buffer,append, jIT会优化: 将append的锁粗化,在while上面加一把锁
对象如何定位: 有二种方式: 句柄池, 直接指针(栈中引用直接指向堆中实例对象的内存地址)hotspot虚拟机使用的是直接指针
句柄池: 栈中引用指向堆中的句柄池,句柄池中保存了实例对象的内存地址和对象类型数据指针, 再通过句柄池中的地址间接找到堆中实例对象
对象如何分配: 此处有一个逃逸分析: 分析对象的作用域是否仅仅在某个方法中, 否则别的方法也引用这个对象,此时叫方法逃逸,如果某个对象是方法的返回值,别的线程会通过调用方法使用这个对象,此时叫线程逃逸,方法中的对象的作用域如果仅仅是在这个方法中, 直接将此对象分配到栈空间, 以便方法运行完毕,隧栈弹出而自动销毁 ,无需垃圾收集器收集
栈上分配失败,会尝试TLAB分配,(TLAB是eden区一部分,堆为每一个线程都分配一个 TLAB空间,用来分配对象,避免多个线程争夺同一块堆空间,TLAB空间小,很容易就满了,)之后就会将新对象,是否可以直接进入老年代,可以的话,就直接分配到老年代, 如果不可以,就会在eden区进行分配
new对象创建过程:
1: 检查这个对象对应的类是否加载 链接 初始化, 没有的话,就在双亲委派机制下 去加载这个类,
2: 为对象分配内存: 计算对象占用空间大小,在堆中划分一块内存给该对象, 划分内存这里有二种方式,如果堆内存空间不规则,虚拟机就维护一个列表,用来记录哪些内存是空闲的, 空闲列表方式. 另一种 指针碰撞,堆空间很规整, 使用过的内存在一边,未使用过的内存在一边,临界点是有一个指针, 这个指针移动对象空间大小即可, 到底使用哪种方法取决于所选择的垃圾收集器是否有压缩的能力决定
3: 处理并发安全问题: 多个对象创建争夺同一块内存区域引发并发安全问题, 解决: 使用 CAS+失败重试,保证原子性, 另一个方法是: TLAB(Thread Local Allocation Buffer) ,jvm为每一个线程在堆上都创建一小块区域内存空间 ,该线程创建对象,分配内存就从各自的TLAB的内存区域开始分配, 如果TLAB对应的内存区域不够用了,此时才会使用 CAS+失败重试
4: 初始化分配到的空间: 内存分配完成之后,对该对象对应的字段赋初始值, 字段类型对应的零值, 比如int: 0, long:0, String "", 这就是对象的半初始化
5: 设置对象的对象头: 设置对象头: 这个对象是哪个类的实例,怎样找到这个类的元数据 ,该对象的hash码,该对象的gc分代年龄(4字节,最大是15),等信息
6: 执行init方法进行初始化: 设置完对象头之后,从虚拟机角度看, 一个新对象已经产生了, 从java程序看 这个对象才刚开始创建,他的构造方法还没有执行, 所有的字段都是默认零值, 接下来执行init方法,
对象的内存布局:
对象头: 包含二部分: clazz pointer(类型指针, 虚拟机通过这个指针确定对象属于哪个类的实例) , mark world (运行时元数据), 有 哈希值, gc分代年龄, 锁状态标志, 线程持有的锁, 偏向锁ID 偏向时间戳, 如果该对象是数组还需要记录数组的长度
实例数据, 这里才是对象的真正存储的有效信息,即我们在程序代码中定义的各种类型的字段内容,无论是父类继承信息,还是子类定义的字段信息
对其填充: 任何对象的大小都必须是8字节的整数倍,不足就对其填充
对象的访问定位: 使用句柄, 直接指针
句柄: 堆中单独一块内存作为句柄池, 栈中的引用指向的是句柄池中 该对象对应的句柄地址, 句柄包含了该该对象对应实例数据, 类型数据信息
直接指针: 栈中的引用直接指向堆中该对象的实例数据, 实例数据中有该对象对应的类型数据信息地址
hotspot使用的是直接指针, 速度快,少了一次句柄池访问
jvm相关自我总结和 VisualVM工具的使用的更多相关文章
- 几个与JVM相关的JDK工具:jps, jstat, jmap
在项目中遇到OOM(Out of Memory)的问题,为了分析内存和JVM的垃圾回收器GC问题,一并把JVM相关的一些工具也研究了一下: jps:Java进程查看工具,实际上它和Unix/Linux ...
- visualvm工具远程对linux服务器上的JVM虚拟机进行监控与调优
文/朱季谦 最近在做了一些JVM监控与调优的事情,算是第一次实践,还比较陌生,故而先把这一次经验简单记下笔记,这样,对后面学习调优方面时,不至于又想不起来了.本文档主要总结在window本地环境远程对 ...
- JVM相关参数配置和问题诊断<转>
原文连接:http://blog.csdn.net/chjttony/article/details/6240457 1.Websphere JVM相关问题诊断: 由JVM引起的Websphere问题 ...
- 3.1日 重温JVM相关信息
1.JDK.JRE.JVM的关系: JDK是java开发的必备工具箱,JDK其中有一部分是JRE,JRE是JAVA运行环境,JVM则是JRE最核心的部分. 2.JVM的组成: JVM由4大部分组成:C ...
- JVM相关知识
Java虚拟机学习分享最近主要在学习JVM相关知识,-知识主要来源<深入理解JAVA虚拟机>,深有感触,结合自己的理解,整理出一些经验,由于篇幅较长,就把链接帖出来,希望对大家有所帮助: ...
- 了解java虚拟机—JVM相关参数设置(2)
1. JVM相关参数设置 JVM相关配置 -XX:+PrintGC 两次次YoungGC,两次FullGC. -XX:+PrintGCDetails 打印GC时的内存,并且在程序结束时打印堆内存使 ...
- Java性能调优—— VisualVM工具基本使用及监控本地和远程JVM进程超详细使用教程
- jvm虚拟机性能监控与故障处理工具
java开发人员肯定知道jdk的bin目录中有java.exe javac.exe这两个命令行工具,但并非所有程序员都了解过jdk的bin目录之中其他命令行的作用.jdk的工具,体积都比较小,这些命令 ...
- JVM性能调优监控工具专题一:JVM自带性能调优工具(jps,jstack,jmap,jhat,jstat,hprof)
性能分析工具jstatjmapjhatjstack 前提概要: JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps.jsta ...
随机推荐
- CVE-2014-3153分析和利用
本文是结合参考资料对CVE-2014-3153的分析,当然各位看官可以看最后的资料,他们写的比我好. 在看CVE-2014-3153之前我们用参考资料4中例子来熟悉下这类漏洞是如何产生的: /** * ...
- SpringBoot+MyBatis练手项目笔记汇总
以下是我在练习SpringBoot+MyBatis训练时候个人一些笔记汇总(可以点击跳转),献丑了,网上很多大佬的文章都比我写的详细,一些好的文章,我会将贴到各个内容中. 1. 插入数据返回id和内部 ...
- StreamReader & StreamWriter
这节讲StreamReader & StreamWriter,这两个类用于操作字符或者字符串,它将流的操作封装在了底层,相对来说用法比较简单,但是它不支持Seek()方法. 先看一下代码: F ...
- Java中浮点数的坑
基本数据类型 浮点数存在误差 浮点数有一个需要特别注意的点就是浮点数是有误差的,比如以下这段代码你觉得输出的什么结果: public class Demo { public static void m ...
- training11.14
7-10 关于堆的判断 (25分) 题目:将一系列给定数字顺序插入一个初始为空的小顶堆H[].随后判断一系列相关命题是否为真.命题分下列几种: x is the root:x是根结点: x and ...
- Linux x86_64与i386区别之 —— 内存寻址
毫无疑问,不管是32位,还是64位处理器,所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是 存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用途不 ...
- jQuery清空元素和克隆元素
1.清空 $(function () { $('#btn').click(function () { $('#ul1').html('') $('#ul1').empty() $('#ul1').re ...
- javaWeb——Servlet(二)
Servelet登录页面步骤: 浏览器访问http://127.0.0.1/login.html 浏览器通过form把账号和密码提交到/login(通过action),附带method="p ...
- 【转载】8.2.1 CPU性能测试工具
(KVM连载) 8.2.1 CPU性能测试工具 01/08/2013master 1 Comment 8.2.1 CPU性能测试工具 CPU是计算机系统中最核心的部件,CPU的性能直接决定了系统的计算 ...
- Tracert 命令
Tracert 命令 Tracert 命令的作用 Tracert命令诊断实用程序通过向目标计算机发送具有不同生存时间的ICMP数据包,来确定至目标计算机的路由,也就是说用来跟踪一个消息从一台计算机到另 ...