杂七杂八的总结---Java基础知识(二)

目录

反射

垃圾回收机制常见算法

搜索算法

回收算法:

JVM内存结构和内存分配

java内存模型

java内存分配

堆和栈区别

引用类型有哪些

强引用

软引用

弱引用

虚引用

类加载器

类加载器的种类

类什么时候被初始化

类初始化的步骤

JVM加载class

如何获取一个类对象

GC机制

为什么有GC

GC回收哪些内存

什么时候回收垃圾

有GC机制为什么还会有内存泄漏

内存溢出

原因

解决方案

String str = "abc"


反射

对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

步骤:先获取要反射类的字节码-----1.Class.forName(className) 2.类名.class 3.this.getClass()

           将字节码中的方法,变量,构造函数等映射成相应的 Method、Filed、Constructor 等类

垃圾回收机制常见算法

搜索算法

回收对象前首先必须发现那些无用的对象,常见搜索算法如下:

        引用计数器算法(已废弃):给对象设置计数器,被引用时加一,引用失效减一,为0时判定为垃圾。

        根搜索算法:通过一些“GC Roots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(Reference Chain),当一个对象没有被 GC Roots 的引用链连接的时候,说明这个对象是不可用的。

回收算法:

        标记-清除算法(Mark-Sweep)(DVM 使用的算法):标记阶段确定要回收的对象并做标记,清除阶段紧随标记阶段,将标记阶段确定不可用的对象清除。

        复制算法(Copying):把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。

        标记-整理算法(Mark-Compact):当垃圾回收的时候,把存活对象往内存的一端移动,然后直接回收边界以外的内存

        分代收集(Generational Collection):根据对象的存活时间把内存分为新生代和老年代,根据各代对象的存活特点,每个代采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记—整理算法。

JVM内存结构和内存分配

java内存模型

内存分为方法区、java栈和java堆

1、方法区是静态分配的,编译器将变量绑定在某个存储位置上,而且这些绑定不会在运行时改变。常数池,源代码中的命名常量、 String 常量和 static 变量保存在方法区。

2、栈:后进先出。一个栈的空间可能是连续的,也可能是不连续的。保存基本数据类型和对象的引用。

3、堆:以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。java对象内存总是在堆中分配的。

java内存分配

1、基础数据类型直接在栈空间分配

2、方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收

3、引用数据类型,需要用 new 来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量

4、方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收

5、局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆
空间区域等待 GC 回收

6、方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放

7、字符串常量在 DATA 区域分配 , this 在堆空间分配

8、数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小

堆和栈区别

栈:1、申请方式:系统自动分配。   

        2、申请后的响应:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 

        3、申请大小限制:栈是向低地址扩展的数据结构,是一块连续的内存的区域。栈的容量是系统预先规定好的,申请空间不能超过栈的剩余空间,因此,能从栈获得的空间较小。

        4、申请效率:系统自动分配,速度较快

        5、存储内容:函数调用时,第一个进栈的是函数调用语句的下一条可执行语句的地址,然后是函数的各个参数,然后是函数中的局部变量。静态变量是不入栈的。本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

        6、数据结构层面:先进后出的数据结构
 

堆:1、手动申请并指明大小。 

        2、 申请后的响应:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链。     

        3、申请大小限制:堆是向高地址扩展的数据结构,是不连续的内存区域。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

        4、申请效率:new 分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便

        5、存储内容:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排

        6、数据结构层面:满足优先队列的数据结构

堆栈都是java用来在随机存取存储器(RAM)存放数据的地方。

栈存取速度快,缺乏灵活性,数据可共享。堆存取速度慢,可动态分配内存。

引用类型有哪些

对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用

对象位于head中,heap 中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。

强引用

最普遍的引用,一个对象拥有强引用,那么垃圾回收器绝不会回收它。----所以强可及对象是全局变量时需要在确定不使用它时赋值为null,不然不能回收。

 public static void main(String[] args) {
        String abc = new String("abc");           //强引用  abc是强对象
        SoftReference<String> softAbc = new SoftReference<>(abc); //软引用
        WeakReference<String> weakAbc = new WeakReference<>(abc); //弱引用
        //此时abc有三个引用,仍然是强可及对象
        abc = null;         //强可及对象变为软可及对象
        softAbc.clear();     //变为弱可及对象
}

软引用

如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。-----内存不足才会清理。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收, Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。

弱引用

一个对象只具有弱引用,那该类就是可有可无的对象,只要该对象被 gc 扫描到了随时都会把它干掉,生命周期更短。------不管内存是否够,扫描到就清除。

可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收, Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

虚引用

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃
圾回收的活动。

虚引用必须和引用队列(ReferenceQueue)联合使用。如果虚引用所引用的对象被垃圾回收, Java 虚拟机就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

类加载器

类加载器的种类

1、根类加载器     C++写的 ,看不到源码

2、扩展类加载器     加载位置 : jre\lib\ext 中

3、系统(应用)类加载器(System\App)     加载位置 : classpath 中

4、自定义加载器(必须继承 ClassLoader)

类什么时候被初始化

1、创建类的实例,也就是 new 一个对象

2、访问某个类或接口的静态变量,或者对该静态变量赋值

3、调用类的静态方法

4、反射(Class.forName("com.lyj.load"))

5、初始化一个类的子类(会首先初始化子类的父类)

6、JVM 启动时标明的启动类,即文件名和类名相同的那个类

类初始化的步骤

1、如果这个类还没有被加载和链接,那先进行加载和链接

2、假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接父类(不适用于接口)

3、假如类中存在初始化语句(如 static 变量和 static 块),那就依次执行这些初始化语句

JVM加载class

JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,类加载器负责在运行时查找和装入类文件中的类。

当 Java 程序需要使用某个类时,JVM 会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class 文件,然后产生与所加载类对应的 Class 对象。加载完成后, Class 对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后 JVM 对类进行初始化。

如何获取一个类对象

1、类型.class        String.class

2、对象.getClass()       "abc".getClass()

3、Class.forName(),例如: Class.forName(“java.lang.String” )

GC机制

为什么有GC

安全性考虑

减少内存泄漏

减少程序员工作量

GC回收哪些内存

内存运行时 JVM 会有一个运行时数据区来管理内存。它主要包括 5 大部分:程序计数器(Program CounterRegister)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap).

程序计数器、虚拟机栈、本地方法栈是每个线程私有的内存空间,随线程而生,随线程而亡,无需考虑内存回收的问题。

GC 主要进行回收的内存是 JVM 中的方法区和堆。

什么时候回收垃圾

见上文垃圾回收机制常见算法

总而言之,对于堆中的对象,主要用可达性分析判断一个对象是否还存在引用,如果该对象没有任何引用就应该被回收。

对于方法区中的常量和类,当一个常量没有任何对象引用它,它就可以被回收了。而对于类,如果可以判定它为无用类,就可以被回收了。

有GC机制为什么还会有内存泄漏

理论上 Java 因为有垃圾回收机制(GC)不会存在内存泄露问题,但实际开发中,可能会存在无用但可达的对象,这些对象不能被 GC 回收,因此也会导致内存泄露的发生。

内存溢出

原因

1、内存中加载的数据量过于庞大,如一次从数据库取出过多数据;--------尽量分页查询数据库

2、集合类中有对对象的引用,使用完后未清空,使得 JVM 不能回收;

3、代码中存在死循环或循环产生过多重复的对象实体;

4、使用的第三方软件中的 BUG;

5、启动参数内存值设定的过小;

解决方案

1、修改 JVM 启动参数,直接增加内存

2、检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误

3、对代码进行走查和分析,找出可能发生内存溢出的位置

String str = "abc"

1、先定义一个名为 str 的对 String 类的对象引用变量: String str;

2、在栈中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的 String 类的对象 o,并将 o 的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象 o。如果已经有了值为"abc"的地址,则查找对象 o,并返回 o 的地址。

3、将 str 指向对象 o 的地址。

即:创建了一个指向 String 类的对象的引用变量 str,这个对象引用变量指向了某个值为"abc"的 String 类

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值