JVM学习第一天(虚拟机的前世今生与与Java的内存区域)
其实说JVM的时候有很多人会懵, 也很不理解,我会写Java代码就可以了,我干嘛要学这个,其实不是的,学习JVM是很有必要性的;
为什么要了解JVM
1:写出更好,更健壮的Java程序;
2:提高Java应用的性能,排除问题;
3:面试
估计很多人面试都会被问到,当然初级是不会的,毕竟面试初级的时候是你会SSM的使用基本就可以了,再会点前端比如Ajax,Jquery,一般进一些小公司,外包,是没有问题的,但是我们想要的仅仅是这样吗?当然不是我们的目标永远只有一个,那就是一线互联网公司;如果要进这些公司那么JVM是必须要了解的;
Sun/Oracle系列的虚拟机

其他公司的虚拟机

运行时数据区域
定义:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分成若干个不同的数据区域;
类型:程序计数器,虚拟机栈,本地方法栈,Java堆,方法区(运行时常量池),直接内存

各个区域的作用:
程序计数器:较小的内存空间,当前线程执行的字节码的行号指示器;各个线程之间独立存储,互不影响;
Java栈:线程私有,生命周期和线程,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量 表,操作数栈,动态链接,方法出口等信息.方法 的执行就对应着栈帧在虚拟机栈中入栈和出栈的过程;栈里面存放着各种基本数据类型和对象的引用(-Xss);
本地方法栈:本地方法栈保存的是native方法的信息,当一个JVM创建的线程调用native方法后,JVM不再为其在虚拟机栈中创建栈帧,JVM只是简单的动态链接并直接调用native方法;
堆:Java堆是Java需要重点关注的一块区域,因为涉及到内存 的分配(new关键字,反射等)与回收(回收算法,收集器等);(-Xms; -Xmx; -Xmn; -XX:NewSize; -XX:MaxNewSize;);
方法区:也叫永久区,用于存储已被虚拟机加载的类信息,常量("xh","000"等),静态变量(static变量)等数据;(-XX:PermSize; -XX:MaxPermSize; -XX:MetaspaceSize; -XX:MaxMetaspaceSize;);
运行时常量池:运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量("xh","000"等)和符号引用;
解释一下参数的含义:
-Xss : 栈的大小
-Xms : 堆的最小值
-Xmx : 堆的最大值
-Xmn :新生代的大小
-XX:NewSize : 新生代最小值
-XX:MaxNewSize : 新生代最大值
在JDK1.7之前和之后永久代的设置是有区别的;
-XX:PermSize : 1.7及之前 : 永久代最小值
-XX:MaxPermSize : 1.7及之前 : 永久代最大值
-XX:MetaspaceSize : 1.7之后 : 永久代最小值
-XX:MaxMetaspaceSize : 1.7之后 : 永久代最大值
版本空间划分变化
1.6 -> 1.7 : 方法区中的运行时常量池从方法中移动到堆中;
1.7 -> 1.8 : 方法区变成了元空间移动到本地内存中;
为什么要把永久代移动出来呢?是因为永久代用于存储类信息,常量,静态变量等数据不是个好主意,很容易遇到内存溢出的问题;,对永久代进行调优是很困难的,同时将元空间与堆的垃圾回收进行隔离,避免永久代引发Full GC和OOM等问题;
直接内存:不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域;
如果使用NIO,这块区域会被频繁使用,在Java堆内可以使用 directByteBuffer对象直接引用操作;
这块内存不受java堆大小限制,但受本机总内存大小的限制,可以通过 MaxDirectMenorySize来设置,默认与堆内存最大值一样,所以也会出现OOM异常;
站在线程的角度看

对于有些人来说学习JVM理念就是一件很枯燥的事情,但是对于有些人来说这些理念就像是打开了一扇新世界的大门,学习本来就是逆水行舟,不进则退,能他人所不能,我们继续学习;
深入辨析堆和栈
功能:
以栈帧的方式存储方法调用过程,并 存储方法调用过程中基本数据类型的变量(四类8种)以及对象的引用变量,其内存分配在栈上,变量出了作用域就会自动释放,
而堆内存用来存储Java中的对象,无论是成员变量,局部变量,还是类变量,他们指向的对象都存储在堆内存中;
线程独享还是共享:
栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存;
堆内存中的对象对所有线程可见,堆内存中的对象可以被所有线程访问;
空间大小:
栈的内存要远远小于堆的内存,栈的深度是有限制的,可能发生StackOverFlowError问题;

package com.dance.ch01; /**
* @Description TODO
* @ClassName SimmpleHeap
* @Author mr.zhang
* @Date 2020/3/25 22:45
* @Version 1.0.0
**/
public class SimpleHeap { private int id; public SimpleHeap(int id) {
this.id = id;
} public void print() {
System.out.println("My id is " + id);
} public static void main(String[] args) {
SimpleHeap s1 = new SimpleHeap(1);
SimpleHeap s2 = new SimpleHeap(2);
s1.print();
s2.print();
} }
方法的出入栈
方法会打包成栈帧,一个栈帧,至少要包含局部变量表,操作数栈和 帧数据区;

栈上分配
跟着函数调用自行销毁,提高性能
就需要:逃逸分析
package com.dance.ch01; /**
* @Description 栈上分配, 逃逸分析带来的性能优化
* @ClassName StackAlloc
* @Author mr.zhang
* @Date 2020/3/26 13:22
* @Version 1.0.0
**/
public class StackAlloc { public static class User {
public int id = 0;
public String name = "dance";
} public static void createUser() {
User user = new User();
user.id = 1;
user.name = "flower";
} public static void main(String[] args) {
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
createUser();
}
long end = System.currentTimeMillis();
System.out.println("创建一亿次对象所需要的时间:" + (end - begin) + "ms");
} }
开启逃逸分析
[GC (Allocation Failure) 2816K->610K(9984K), 0.0008714 secs]
创建一亿次对象所需要的时间:6ms
关闭逃逸分析
[GC (Allocation Failure) 3448K->632K(9984K), 0.0001884 secs]
[GC (Allocation Failure) 3448K->632K(9984K), 0.0001487 secs]
[GC (Allocation Failure) 3448K->632K(9984K), 0.0001705 secs]
........
创建一亿次对象所需要的时间:1485ms
JVM启动参数
-server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations -XX:-UseTLAB
参数讲解:
-server : JVM运行的一种模式 JVM模式分为两种 Client和Server 只有在server模式下 才能使用逃逸分析
-Xmx -Xms : 堆的最大和最小分配
-XX:+DoEscapeAnalysis : 启用逃逸分析
-XX:+PrintGC 打印GC日志
-XX:-EliminateAllocations 标量替换 是否允许被打散分配到栈上
-XX:-UseTLAB TLAB 全称 ThreadLocalAllocBuffer, 事先在堆中为每个线程分配一块私有内存,代表只有特定的线程在特定的区域分配,不会引发竞争,提高性能,但是允许被访问
如果想要使用的话 逃逸分析 和 标量替换 都是必须要开启的;任意关掉一个 都不会启用 当然 JVM必须是Server模式
JDK 1.8后默认开启逃逸分析,如果可能大家最好还是采用1.8及其之后的版本
虚拟机中的对象

对象的内存布局

对象的访问定位

Hotspot就是用的直接指针;
最后写几个小栗子吧;
堆溢出:
package com.dance.ch01; import java.util.LinkedList;
import java.util.List; /**
* @Description 堆溢出
* @ClassName OOM
* @Author mr.zhang
* @Date 2020/3/26 14:38
* @Version 1.0.0
**/
public class OOM { private static List<Object> objects = new LinkedList<Object>(); public static void main(String[] args) {
int i = 0;
while (true){
i++;
if(i%10000==0){
System.out.println(i);
}
objects.add(new Object());
}
} }
JVM参数:-Xms5m -Xmx5m -XX:+PrintGC
[Full GC (Allocation Failure) 5952K->5952K(5952K), 0.0153244 secs]
[Full GC (Allocation Failure) 5952K->5950K(5952K), 0.0169047 secs]
..............
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
当然这只是一个例子,OOM引发的方式也不同,溢出的区域也不同,提示信息也不同,就我之前在项目中碰见过,有人居然在不理解有些网上的工具类的时候就直接使用,导致把2G大小的电子文件直接塞入到内存中,导致引发OOM,而且排查的时候,很不好排查,所以对于有些代码中写完最好,好好测试一下,尤其是IO操作最容易应该OOM问题;
栈溢出:
说一下我见过的吧;
最容易出现的就是树的算法:我们一般对于机构,部门,权限树;这种算法,我们最常用的就是递归,其实这种方式是最容易出现栈溢出,就是当我们的机构,权限的树越来越复杂,就会出现递归中的栈帧越来越大,就会导致栈溢出;所以我们在做树的算法的时候推荐使用非递归实现;
作者:彼岸舞
时间:2020\03\26
内容关于:JVM
本文部分来源于网络,只做技术分享,一概不负任何责任
JVM学习第一天(虚拟机的前世今生与与Java的内存区域)的更多相关文章
- JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇
JVM学习第一篇思考:一个Java代码是怎么运行起来的-上篇 作为一个使用Java语言开发的程序员,我们都知道,要想运行Java程序至少需要安装JRE(安装JDK也没问题).我们也知道我们Java程序 ...
- 深入探究JVM(1) - Java的内存区域解析
http://blog.csdn.net/sczyh22/article/details/46652901<br>Java 虚拟机在执行Java程序的时候会把它管理的内存区域划为几部分,这 ...
- jvm学习第一天
视频教程链接 第一部分-jvm初识 0.jvm概览图 JVM由三个主要的子系统构成 类加载子系统 运行时数据区(内存结构) 执行引擎运行时数据区(内存结构) 1.什么是jvm 定义: ①. JVM 是 ...
- JVM学习笔记:虚拟机的类加载机制
JVM类加载机制分两部分来总结: (1)类加载过程 (2)类加载器 一.JVM类加载过程 类的加载过程:加载 →连接(验证 → 准备 → 解析)→ 初始化. 类的生命周期:加载 →连接(验证 → 准备 ...
- jvm学习笔记:虚拟机栈
虚拟机栈 Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same ...
- JVM学习笔记:虚拟机性能监控
JDK中除了包含与开发密切相关的jar包外,还包含了很多非常实用的工具.在%JAVA_HOME%\bin\目录下面除了命令行工具外,还包括了几个强大的可视化工具.这些工具可以辅助我们开发.调试应用程序 ...
- JVM学习(一)Java虚拟机运行时数据区域
一.Java内存区域 1.运行时数据区域 根据<Java 虚拟机规范(Java SE 7 版)>规定,Java 虚拟机所管理的内存包括以下几个运行时数据区域: 1.1 程序计数器 程序计数 ...
- JVM学习笔记(一):Java虚拟机和虚拟机内存区域
为什么Java程序需要运行在虚拟机上 因为Java在设计之初的跨平台特性,我们知道Java程序是运行在Java虚拟机上的.如果你要问为什么Java程序要运行在虚拟机上,我可以反问你几个问题. 为什么买 ...
- java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
随机推荐
- MATLAB通过ODBC连接数据库方法
MATLAB通过ODBC连接数据库方法 1.首先创建数据库,我在这里用到的是MySQL 8.0 2.建立ODBC数据源,参考链接: https://www.cnblogs.com/benpao1314 ...
- 禁用 Spring Boot 中引入安全组件 spring-boot-starter-security 的方法
1.当我们通过 maven 或 gradle 引入了 Spring boot 的安全组件 spring-boot-starter-security,Spring boot 默认开启安全组件,这样我们就 ...
- eclipse中启动tomcat出现错误的解决方法
前段时间跟着老师做课设,各方面调试都没有问题.近段时间想起来,看看之前写过的代码,翻着翻着就发现启动tomcat出现了错误 错误如下: 错误原因:tomcat路径配置有问题,之前可能配置好了然后由于种 ...
- 2020-05-24:ZK分布式锁有几种实现方式?各自的优缺点是什么?
福哥答案2020-05-24: Zk分布式锁有两种实现方式一种比较简单,应对并发量不是很大的情况.获得锁:创建一个临时节点,比如/lock,如果成功获得锁,如果失败没获得锁,返回false释放锁:删除 ...
- 2020-04-24:Object obj = new Object()这句话在内存里占用了多少内存
福哥答案2020-04-25:这道题最好把对象和变量分开说明,否则容易产生误解.以下都是64位环境下.针对对象:压缩状态:MarkWord 8+klass 4+数据0+对齐4=16非压缩状态:Mark ...
- 在虚拟机中安装Mysql
目录 下载Mysql 安装 配置mysql允许远程访问 下载Mysql 下载地址:http://dev.mysql.com/downloads/mysql 我这里下载的是安装版本 安装 配置mysql ...
- CompletableFuture异步线程
1.线程池七大参数介绍 (1)corePoolSize:线程池中常驻核心线程数 (2)maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1 (3)keepAliveT ...
- 涨见识!Java String转int还有这种写法
先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个有颜值却假装靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题 ...
- 【期外】(二)还是N皇后动画演示
题目:n皇后题目 题解:n皇后题解 演示:
- [源码解析] 当 Java Stream 遇见 Flink
[源码解析] 当 Java Stream 遇见 Flink 目录 [源码解析] 当 Java Stream 遇见 Flink 0x00 摘要 0x01 领域 1.1 Flink 1.2 Java St ...