上次博客,我们说了jvm运行时的内存模型,堆,栈,程序计数器,元空间和本地方法栈。我们主要说了堆和栈,栈的流程大致也说了一遍,同时我们知道堆是用来存对象的,分别年轻代和老年代。但是具体的堆是怎么来存放对象的呢?什么时候可以将对象放置在老年代呢。下面我来看一下。

  

如果都为默认设置,大致就是这样的。假设我们设置内存堆的大小为600M,那么老年代就大概是400M,我们的年轻代就是200M,然后年轻代的eden区域占160M也就是200M的8/10,一般新建的对象都在这,我是说一般啊。后面会用这个600M来详细说明,from和 to区域各占20M,也就是Survivor区域占用40M,每次做完minor GC,对象就放在这个区域。

  我刚才说到,有时候对象不在年轻代,那么我来具体分析一下,什么情况放置在年轻代,而什么时候又放置在老年代。

1,Minor GC之后,存活的对象Survivor区域放不下。

public class Main {
public static void main(String[] args) {
byte[] bt1;
bt1 = new byte[60000 * 1024];
}
}

加入堆内存日志,我们得到打印结果为:

  我们得到bt1新建以后,我们的堆内存几乎占满了,现在已经99%了,那么我们再来看一下。

public class Main {
public static void main(String[] args) {
byte[] bt1,bt2;
bt1 = new byte[60000 * 1024];
bt2 = new byte[10000 * 1024];
}
}

从代码里我们可以得知,我们新建了bt1之后又新建了bt2,这时我们的eden区域应该不够用了,那么我们的内存会怎么来处理呢。我们来看一下结果

  我们可以看到已经做了一次GC了,但是还是放不下,那么我们直接将较大的对象直接放置在了堆内存上。

2,长期存活的对象移到老年代。也就是经过多次minorGC以后,对象还是存活的,我们将该对象移置老年代,一般是15次,也就是对象头内的分代年龄达到15岁时,我们将该对象移置老年代。

3,对象动态年龄判断。

  这个很重要的一个理论知识,大概来说一下,当我们做完minorGC以后,对象放在to区域,也就是我们Survivor的to区域,可能对象是放不下的,这时会来计算分类年龄,大致是这样来算的将所有分代年龄为1的相加,再加上分代年龄为2的,再加分代年龄为3的,依次相加,一直加到最大的分代年龄,但在相加过程中,你会发现加到分代年龄为m的对象,总大小已经放满了to区域,这时就将m到n分代年龄的对象都移置到老年代,包含m。也就是大于Survivor区域的50%时,则后面的对象,包含该年龄的对象都放置在老年代。

4,大对象直接放在老年代。再来看段代码。

public class Main {
public static void main(String[] args) {
byte[] bt1;
bt1 = new byte[90000 * 1024];
}
}

  上面我知道我们创建一个大概600M的对象放置在eden时,占了99%,那么我们创建大于600M的对象,eden一定放不下了。那么直接放置在老年代。这里参数也是可以设置的。我来设置一个参数再看看,设置参数为

-XX:PretenureSizeThreshold=10000000 -XX:+UseSerialGC -XX:+PrintGCDetails   

public class Main {
public static void main(String[] args) {
byte[] bt1;
bt1 = new byte[20000 * 1024];
}
}

  我们设置了参数,声明10M的对象就为大对象,我们创建了一个大概20M的对象,就直接放置在了老年代上。就是对象经历那么多次的minorGC了,jvm虚拟机会认为你可能会一直存活,趁着这次放不下了,你就趁早过来吧,来我们老年代混吧。

5,老年代空间分配担保机制。

  其实我们每次进行minorGC前,会有一系列操作的,可能会进行full GC的,那么我们来看一下流程吧。

我来解释一下上面那个五彩缤纷的图。等我们的eden区满时,需要进行minorGC,这时会优先看一下老年代的剩余空间大小,如果老年代剩余的空间不多了,我们就可能进行full GC,也就是我们老年代的剩余空间小于我们的eden区内将要进行minorGC对象的总和。

如果真的小了,那么我们往下走,我们会判断时候配置了-XX:-HandlePromotionFailure (jdk8以上默认设置)这个参数,如果没配置,直接进行fullGC,如果配置了就去判断老年代的剩余空间是否小于我们每次minorGC后每次要放在老年代对象大小的平均值,如果老年代小于minorGC了,那么进行fullGC。否则不需要进行full GC。

eden和Survivor(from和to)默认比例是8:1:1,但是jvm可能会将我们的参数优化,也就是-XX:+UseAdaptiveSizePolicy这个默认参数,我将其改为-XX:-UseAdaptiveSizePolicy不进行优化,保持8:1:1的比例了。

  我们再来看一下什么样的对象是可以被回收的。

1,引用计数法(基本不用,循环引用对象永远无法销毁,可能内存溢出)

  给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用 失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。

  GC Roots根节点一般为线程栈的本地变量、静态变量、本地方法栈的变量等等。

2,可达性分析算法。

  这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点, 从这些节点开始向下搜索,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象

3,常见的引用类型。

  java的引用类型一般分为四种:强引用、软引用、弱引用、虚引用

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference; public class Main {
public static void main(String[] args) {
User user = new User();//强引用
WeakReference<User> user2 = new WeakReference<User>(new User());//弱引用
SoftReference<User> user3 = new SoftReference<User>(new User());//软引用
}
}

  一般将对象用SoftReference软引用类型的对象包裹,正常情况不会被回收,但是GC做完后发现释放不出空间存放新的对象,则会把这些软引用的对象回收掉。软引用可用来实现内存敏感的高速缓存。

4,finalize最终判断对象存活。

  finalize是在对象马上要被收回之前运行的最后一个方法,可以写逻辑,但是完全不建议去这样去写,很可能出现对象永远不会被回收,造成内存溢出,也就是说在finalize方法内还可能“救活”我们的对象。

  即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。

  如何判断一个类是无用的类

1.该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何 实例。

2.加载该类的 ClassLoader 已经被回收。

3.该类对应的 java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

  最后我们来看一下逃逸分析。

    JVM的运行模式用三种,分别是解释模式,编译模式和混合模式,这里简单说一下这个问题,不然后面会蒙圈的。

解释模式就是执行一行JVM字节码就编译一行为机器码,这样的好处就是很节省内存空间,不用把所有的字节码都塞到内存里面去,运行效率低,但是启动快。

编译模式和解释模式恰恰相反,是先将所有JVM字节码一次编译为机器码,然后一次性执行所有机器码。这样会提高我们的运行效率,但是消耗空间资源。

混合模式是上面的总和,依然使用解释模式执行代码,但是对于一些 "热点" 代码采用编译模式执行,JVM一般采用混合模式执行代码。

我们来看一段代码。

public class Main {
public User getUserBeanTest() {
User user = new User();//放置在堆上
return user;
} public void userBeanTest() {
User user = new User();//优先和方法一起发放置在栈上.
}
}

也就是说明,对象也是有很小的可能放置在栈上的。中秋放假了,明天补一下mybatis的底层是实现原理。过几天继续来说我们的jvm优化

最进弄了一个公众号,小菜技术,欢迎大家的加入

java架构之路-(11)JVM的对象和堆的更多相关文章

  1. java架构之路-(JVM优化与原理)JVM类的加载机制

    话不多说,先上图. ***.class文件执行大概就是这样来走的.我们都知道我们的java文件经过编译以后会生成对应的class文件.先经过类装载子系统,然后塞进运行时内存模型的元空间,开始执行方法, ...

  2. java架构之路-(JVM优化与原理)JVM之G1回收器和常见参数配置

    过去的几天里,我把JVM内部的垃圾回收算法和垃圾回收器.还剩下最后一个G1回收器没有说,我们今天数一下G1回收器和常见的参数配置. G1回收器 G1 (Garbage-First)是一款面向服务器的垃 ...

  3. java架构之路-(JVM优化与原理)JVM的运行时内存模型

    还是我们上次的图,我们上次大概讲解了类加载子系统的执行过程,验证,准备,解析,初始化四个过程.还有我们的双亲委派机制. 我们这次来说一下运行时内存模型.上一段小代码. public class Mai ...

  4. [转帖]java架构之路-(面试篇)JVM虚拟机面试大全

    java架构之路-(面试篇)JVM虚拟机面试大全 https://www.cnblogs.com/cxiaocai/p/11634918.html   下文连接比较多啊,都是我过整理的博客,很多答案都 ...

  5. java架构之路-(面试篇)JVM虚拟机面试大全

    下文连接比较多啊,都是我过整理的博客,很多答案都在博客里有详细说明,理解记忆是最扎实的记忆.而且我的答案不一定是最准确的,但是我的答案不会让你失望,而且几乎每个答案都是问题的扩展答案. 1.JVM内存 ...

  6. [Java开发之路](9)对象序列化与反序列化

    1. 对象序列化 当你创建对象时.仅仅要你须要.它会一直存在,可是程序终止时,不管何时它都不会继续存在.虽然这样做是很有意义的,可是在某些情况下.假设程序不执行时扔能存在而且保存其信息,那将对我们很实 ...

  7. java架构之路-(12)JVM垃圾回收算法和垃圾回收器

    接上次JVM虚拟机堆内存模型来继续说,上次我们主要说了什么时候可能把对象直接放在老年代,还有我们的可能性分析,提出GCroot根的概念.这次我们主要来说说垃圾回收所使用的的算法和我们的垃圾回收器,需要 ...

  8. java架构之路-(Redis专题)聊聊大厂那些redis

    上几次说了redis的主从,哨兵,集群配置,但是内部的选举一直没说,先来简单说一下选举吧. 集群选举 redis cluster节点间采取gossip协议进行通信,也就是说,在每一个节点间,无论主节点 ...

  9. java架构之路-(Redis专题)redis面试助力满分+

    1.Redis支持的数据类型? 答:五种,在第一节redis相关的博客我就说过,String,Hash,List,Set,zSet,也就是我们的字符串,哈希,列表,集合,有序集合五种.结构图如下. 2 ...

随机推荐

  1. python3学习-logging模块

    1.logging模块的使用非常简单,引入模块就可以使用. import logging logging.debug('This is debug message') logging.info('Th ...

  2. koa2图片上传成功后返回服务器地址,实时显示服务器图片

    版本:node(8.5.0); koa(2.4.1); koa-router(7.3.0); koa-body(2.5.0); koa-static(4.0.2); 代码实现 const fs = r ...

  3. POI通用导出Excel数据(包括样式设计)

    前言 前一段时间我写过通用的导入Excel,前几天也写了导出pdf格式的,还有我之前搞得导出Word,我在之前的博客也都介绍了导出和导入是一个道理,无非是一个获取一个是赋值.昨天有一位同仁看了我的Ex ...

  4. Spring Boot 统一异常这样处理和剖析,安否?

    话说异常 「欲渡黄河冰塞川,将登太行雪满天」,无论生活还是计算机世界难免发生异常,上一篇文章RESTful API 返回统一JSON数据格式 说明了统一返回的处理,这是请求一切正常的情形:这篇文章将说 ...

  5. 手撕ThreadPoolExecutor线程池源码

    这篇文章对ThreadPoolExecutor创建的线程池如何操作线程的生命周期通过源码的方式进行详细解析.通过对execute方法.addWorker方法.Worker类.runWorker方法.g ...

  6. 纯 CSS 实现绘制各种三角形(各种角度)

    一.前言 三角形实现原理:宽度width为0:height为0:(1)有一条横竖边(上下左右)的设置为border-方向:长度 solid red,这个画的就是底部的直线.其他边使用border-方向 ...

  7. shiro@RequiresPermission的设置

    public class MyShiroRealm extends AuthorizingRealm { //slf4j记录日志,可以不使用 private Logger logger = Logge ...

  8. Redis学习总结(七)--Redis集群之客户端访问

    我们来试试进行数据的存储 127.0.0.1:7000> set name marklogzhu OK 127.0.0.1:7000> get name "marklogzhu& ...

  9. Egret白鹭开发小游戏之自定义load加载界面

    刚接触不久就遇到困难------自定义loading.想和其他获取图片方式一样获取加载界面的图片,结果发现资源还没加载就需要图片,在网上百度了许多,都没有找到正确的方式,通过自己的摸索,终于,,,我成 ...

  10. CodeForces 909C

    题意略. 思路: 开始的时候,定义dp[i]:当前行在第i行,i~n有多少种排列方式,如果i为f,那么dp[i] = dp[i + 1],因为第i + 1条语句只能放在f后且向右缩进一位: 如果i为s ...