JAVA入门——lesson 5

面向对象的内存分析

JAVA 虚拟机内存模型概念

在这里插入图片描述
在这里插入图片描述
Java 虚拟机的内存可以分为三个区域:栈 stack、堆 heap、方法区 method area。
虚拟机栈(简称:栈)的特点如下:

  1. 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变 量、操作数、方法出口等)
  2. JVM 为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变 量等)
  3. 栈属于线程私有,不能实现线程间的共享!
  4. 栈的存储特性是“先进后出,后进先出” 5. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!

堆的特点如下:

  1. 堆用于存储创建好的对象和数组(数组也是对象)
  2. JVM 只有一个堆,被所有线程共享
  3. 堆是一个不连续的内存空间,分配灵活,速度慢!
  4. 堆被所有的线程所共享,在堆上的区域,会被垃圾回收器做进一步划分,例如新生代、老年代的划分。
    在这里插入图片描述
    方法区(也是堆)特点如下:
    1.方法区是 JAVA 虚拟机规范,可以有不同的实现。
    i. JDK7 以前是“永久代”
    ii. JDK7 部分去除“永久代”,静态变量、字符串常量池都挪到了堆内存中
    iii. JDK8 是“元数据空间”和堆结合起来。
    2. JVM只有一个方法区,被所有线程共享!
    3. 方法区实际也是堆,只是用于存储类、常量相关的信息!
    4.用来存放程序中永远是不变或唯一的内容。(类信息【Class 对象,反射机制中会 重点讲授】、静态变量、字符串常量等)
    5.常量池主要存放常量:如文本字符串、final 常量值

【示例 】编写 Person 类并分析内存

public class Person {
    String name;
    int age;
    public void show(){
        System.out.println(name);
    }
    public static void main(String[ ] args) {
        // 创建p1对象
        Person p1 = new Person();
        p1.age = 24;
        p1.name = "张三";
        p1.show();
        // 创建p2对象
        Person p2 = new Person(); 
        p2.age = 35; 
        p2.name = "李四"; 
        p2.show(); 
        Person p3 = p1; 
        Person p4 = p1; 
        p4.age = 80; 
        System.out.println(p1.age); 
    } 

}

在这里插入图片描述

参数传值机制

Java 中,方法中所有参数都是“值传递”,也就是“传递的是值的副本”。(我们得到的是“原参数的复印件,而不是原件”。)
· 基本数据类型参数的传值
传递的是值的副本。 副本改变不会影响原件。
· 引用类型参数的传值
传递的是值的副本。但是引用类型指的是“对象的地址”。因此,副本和原参数都指向 了同一个“地址”,改变“副本指向地址对象的值,也意味着原参数指向对象的值也发生了 改变”。
【示例】多个变量指向同一个对象

public class test public class User {
    int id; //id
    String name; //账户名
    String pwd; //密码
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public static void main(String[ ] args) {
        User u1 = new User(100, "吴小美");
        User u3 = u1;
        System.out.println(u1.name);
        u3.name="张三";
        System.out.println(u1.name); 
    } 
}

垃圾回收机制(Garbage Collection)

Java 引入了垃圾回收机制,Java 程序员可以将更多的精力放到业务逻辑上而不是内存管理工作上,大大的提高了开发效率。

垃圾回收原理和算法

  • 内存管理

Java 的内存管理很大程度就是:堆中对象的管理,其中包括对象空间的分配和释放。
对象空间的分配:使用 new 关键字创建对象即可对象空间的释放:将对象赋值 null 即可。
·

  • 垃圾回收过程

任何一种垃圾回收算法一般要做两件基本事情:

  • 发现无用的对象

  • 回收无用对象占用的内存空间。
    垃圾回收机制保证可以将“无用的对象”进行回收。
    无用的对象指的就是没有任何变量引用该对象。Java 的垃圾回收器通过相关算法发现 无用对象,并进行清除和整理。

  • 垃圾回收相关算法

  1. 引用计数法
    堆中的每个对象都对应一个引用计数器,当有引用指向这个对象时,引用计数器加
    1,而当指向该对象的引用失效时(引用变为 null),引用计数器减 1,最后如果该 对象的引用计算器的值为 0 时,则 Java 垃圾回收器会认为该对象是无用对象并对其 进行回收。优点是算法简单,缺点是“循环引用的无用对象”无法别识别。
    在这里插入图片描述
    【示例】循环引用演示
    代码中,s1 和 s2 互相引用对方,导致他们引用计数不为 0,但是实际已经无用,但无法被识别。
public class Student {
    String name;
    Student friend;
    public static void main(String[ ] args) {
        Student s1 = new Student();
        Student s2 = new Student();
        s1.friend = s2;
        s2.friend = s1;
        s1 = null;
        s2 = null; 
    } 
}
  1. 引用可达法(根搜索算法)

程序把所有的引用关系看作一张图,从一个节点 GC ROOT 开始,寻找对应的引用 节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找 完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

通用的分代垃圾回收机制

在这里插入图片描述
分代垃圾回收机制,是基于这样一个事实:不同的对象的生命周期是不一样的。因此, 不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。我们将对象分为三种状 态:年轻代、年老代、永久代。同时,将处于不同状态的对象放到堆中不同的区域。

  1. 年轻代
    所有新生成的对象首先都是放在 Eden 区。年轻代的目标就是尽可能快速的收集掉 那些生命周期短的对象,对应的是 Minor GC,每次 Minor GC 会清理年轻代的内存, 算法采用效率较高的复制算法,频繁的操作,但是会浪费内存空间。当“年轻代”区域 存放满对象后,就将对象存放到年老代区域。
  2. 年老代
    在年轻代中经历了 N(默认 15)次垃圾回收后仍然存活的对象,就会被放到年老代 中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。年老代对象越来越 多,我们就需要启动 Major GC 和 Full GC(全量回收),来一次大扫除,全面清理年轻 代区域和年老代区域。
  3. 永久代
    用于存放静态文件,如 Java 类、方法等。持久代对垃圾回收没有显著影响。JDK7 以前就是“方法区”的一种实现。JDK8 以后已经没有“永久代”了,使用 metaspace 元数据空间和堆替代。
    在这里插入图片描述
    ·Minor GC:
    用于清理年轻代区域。Eden 区满了就会触发一次 Minor GC。清理无用对象,将有用 对象复制到“Survivor1”、“Survivor2”区中。
    ·Major GC
    用于清理老年代区域。
    ·Full GC: 用于清理年轻代、年老代区域。 成本较高,会对系统性能产生影响。

JVM 调优和 Full GC

在对 JVM 调优的过程中,很大一部分工作就是对于 Full GC 的调节。有如下原因可能 导致 Full GC:

  1. 年老代(Tenured)被写满
  2. 永久代(Perm)被写满
  3. System.gc()被显式调用
  4. 上一次 GC 之后 Heap 的各域分配策略动态变化

开发中容易造成内存泄露的操作

内存泄漏:
指堆内存由于某种原因程序未释放,造成内存浪费,导致运行速度减慢甚至系统崩溃等。
在这里插入图片描述

tip:
 在实际开发中,经常会造成系统的崩溃。如下这些操作我们应该注意这些使用场景。请大家学完相关内容后,回头过来温习下面的内容。不要求此处掌握相关细节。

如下四种情况时最容易造成内存泄露的场景,请大家开发时一定注意:

  1. 创建大量无用对象
    比如:大量拼接字符串时,使用了 String 而不是 StringBuilder。
String str = ""; 
for (int i = 0; i < 10000; i++) { 
str += i; 	//相当于产生了 10000 个 String 对象 
}
  1. 静态集合类的使用
    像 HashMap、Vector、List 等的使用最容易出现内存泄露,这些静态变量的生命周期 和应用程序一致,所有的对象也不能被释放。

3. 各种连接对象(IO 流对象、数据库连接对象、网络连接对象)未关闭
IO 流对象、数据库连接对象、网络连接对象等连接对象属于物理连接,和硬盘或者网 络连接,不使用的时候一定要关闭。

4. 监听器的使用不当
释放对象时,没有删除相应的监听器

其他要点

  1. 程序员无权调用垃圾回收器。
  2. 程序员可以调用 System.gc(),该方法只是通知 JVM,并不是运行垃圾回收器。尽量 少用,会申请启动 Full GC,成本高,影响系统性能。
  3. Object 对象的 finalize 方法,是 Java 提供给程序员用来释放对象或资源的方法,但 是尽量少用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值