zoukankan      html  css  js  c++  java
  • 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问题;

     1 package com.dance.ch01;
     2 
     3 /**
     4  * @Description TODO
     5  * @ClassName SimmpleHeap
     6  * @Author mr.zhang
     7  * @Date 2020/3/25 22:45
     8  * @Version 1.0.0
     9  **/
    10 public class SimpleHeap {
    11 
    12     private int id;
    13 
    14     public SimpleHeap(int id) {
    15         this.id = id;
    16     }
    17 
    18     public void print() {
    19         System.out.println("My id is " + id);
    20     }
    21 
    22     public static void main(String[] args) {
    23         SimpleHeap s1 = new SimpleHeap(1);
    24         SimpleHeap s2 = new SimpleHeap(2);
    25         s1.print();
    26         s2.print();
    27     }
    28 
    29 }

    方法的出入栈

    方法会打包成栈帧,一个栈帧,至少要包含局部变量表,操作数栈和 帧数据区;

    栈上分配

    跟着函数调用自行销毁,提高性能

    就需要:逃逸分析

     1 package com.dance.ch01;
     2 
     3 /**
     4  * @Description 栈上分配, 逃逸分析带来的性能优化
     5  * @ClassName StackAlloc
     6  * @Author mr.zhang
     7  * @Date 2020/3/26 13:22
     8  * @Version 1.0.0
     9  **/
    10 public class StackAlloc {
    11 
    12     public static class User {
    13         public int id = 0;
    14         public String name = "dance";
    15     }
    16 
    17     public static void createUser() {
    18         User user = new User();
    19         user.id = 1;
    20         user.name = "flower";
    21     }
    22 
    23     public static void main(String[] args) {
    24         long begin = System.currentTimeMillis();
    25         for (int i = 0; i < 100000000; i++) {
    26             createUser();
    27         }
    28         long end = System.currentTimeMillis();
    29         System.out.println("创建一亿次对象所需要的时间:" + (end - begin) + "ms");
    30     }
    31 
    32 }

    开启逃逸分析

    [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就是用的直接指针;

    最后写几个小栗子吧;

    堆溢出:

     1 package com.dance.ch01;
     2 
     3 import java.util.LinkedList;
     4 import java.util.List;
     5 
     6 /**
     7  * @Description 堆溢出
     8  * @ClassName OOM
     9  * @Author mr.zhang
    10  * @Date 2020/3/26 14:38
    11  * @Version 1.0.0
    12  **/
    13 public class OOM {
    14 
    15     private static List<Object> objects = new LinkedList<Object>();
    16 
    17     public static void main(String[] args) {
    18         int i = 0;
    19         while (true){
    20             i++;
    21             if(i%10000==0){
    22                 System.out.println(i);
    23             }
    24             objects.add(new Object());
    25         }
    26     }
    27 
    28 }

    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问题;

    栈溢出:

      说一下我见过的吧;

      最容易出现的就是树的算法:我们一般对于机构,部门,权限树;这种算法,我们最常用的就是递归,其实这种方式是最容易出现栈溢出,就是当我们的机构,权限的树越来越复杂,就会出现递归中的栈帧越来越大,就会导致栈溢出;所以我们在做树的算法的时候推荐使用非递归实现;

    作者:彼岸舞

    时间:2020326

    内容关于:JVM

    本文部分来源于网络,只做技术分享,一概不负任何责任

  • 相关阅读:
    lines-HDU5124(区间处理 +离散化)
    Reorder the Books-HDU5500
    Bad Hair Day-POJ3250(简单的入栈出栈)
    Count the Colors-ZOJ1610(线段树区间求)
    Just a Hook-HDU1698(线段树求区间)
    Mayor's posters-POJ2528(线段树+离散化)
    A Simple Problem with Integers-POJ3468
    Strongly connected-HDU4635
    Caocao's Bridges-HDU4738(Tarjin+求桥)
    Warm up-HUD4612(树的直径+Tarjin缩点)
  • 原文地址:https://www.cnblogs.com/flower-dance/p/12574946.html
Copyright © 2011-2022 走看看