程序在JVM是如何执行?为什么要GC?垃圾回收算法

程序计数器:

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
程序计数器处于线程的独占区。
如果线程执行的是java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果执行的是native方法,这个计数器的值为undefined(不明确的)
此区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域(内存溢出)。
每一个线程都有自己的独立的程序计数器。

虚拟机栈:

java虚拟机栈描述的是java方法执行的动态内存模型。
栈帧:
每一个方法执行,都会创建一个栈帧,伴随着方法从创建到执行完成。用于存储局部变量表,操作数栈,动态链接,方法出口等。
在这里插入图片描述
局部变量表:
存放编译期可知的各种基本数据类型,引用类型,returnAddress类型。
局部变量表的内存空间在编译时期就会完成分配,当进入一个方法时,这个方法需要在帧分配多少内存是固定的,在方法运行期间是不会改变局部变量表的大小。
因为我们局部变量表是存在栈中的,我们存放的不是对象,是对象的引用。这个引用的大小是不会变化的。
java虚拟机栈的大小:
栈是有大小限制,存放大量的栈帧后会有StackOverflowError栈内存溢出。
OutOfMemory如果栈内存不限制大小,程序会不停的一直递归调用,直至将内存放的超出jvm设定的虚拟机栈的大小,无法申请到内存,然后会抛内存溢出。
测试用例:

public class Test {
    public static void main(String[] args) {
        func();
    }
    private static void func(){
        System.out.println("正在执行》》》");
        func();
    }
}

在这里插入图片描述

本地方法栈:

虚拟机栈为虚拟机执行java方法服务。
本地方法栈是虚拟机为虚拟机执行native方法提供服务。

java堆

存放对象实例
垃圾收集器管理的主要区域
新生代,老年代,Eden空间
OutOfMemory.

JVM概述:

JVM在哪?
JVM是什么?
在这里插入图片描述
站在OS角度上看待JVM。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
关于类加载时的初始化:
1.所有的静态属性默认值都是0或者0的变形(null,false)
JVM在内存上分配空间时,先调用memset(这块内存,0x0);

2.按照代码书写顺序,进行静态属性初始化
—1.static int a=10;直接初始化
—2.static { a=10; }静态代码块

静态方法和普通方法的区别:

在这里插入图片描述
静态方法和普通方法都是被调用时才执行。
什么时候执行不是他们两个的区别

真正的区别:
调用时的形参不同(隐式参数)
形参是实参的复制。
在这里插入图片描述

变量:

怎么理解变量?变量就是内存上的一段空间,只是根据变量的各种特征,来看怎么具体解释这段内存

类型 名称 = 值;
在这里插入图片描述
类型:这段内存到底有多长:如何解释
int 32 bit
long 64 bit
int a=666;
就代表是666的值
String s=666;
JVM会根据666区堆上找对应的对象去。

类型:两大类:基本数据类型和引用数据类型。变量类型能决定变量在内存的哪个区域上吗?
回答:不可以

形态:局部变量,形参,属性,静态属性。
只有变量的形态才能决定变量在内存的哪一个区域上。

局部变量,形参属于栈帧的一部分,而栈帧时在栈上的
属性是属于对象的一部分,而对象在堆上。
静态属性属于类的一部分,而类在方法区上

class Person{
	String name;都是引用,因为是属性所以在堆上
}
class Main{
public static void main(String []args){
		String name;都是引用,但是局部变量,所以是在栈上的
	}
}

1.程序是如何运行起来的:

  1. 首先把需要运行的类,都过类加载器放置到内存中(方法区)中。
  2. 执行引擎执行类中的方法代码 —执行过程中需要栈的辅助 – 中途会用到堆中的对象等数据
    栈是用来做什么的?
    栈是用来存储栈帧的,栈帧是描述执行到哪一个方法,栈帧中有一个重要的结构就是局部变量表(存储局部变量和形参)。
    堆是用来做什么的?
    堆就是进行存储对象的
    方法区中存放的是什么?
    方法区存放的是编译好的字节码,存放静态属性
    方法区中保存的就是指令:
    load a 读取a的值
    load b 读取b的值
    add * a * b 进行运算
    save r 最后赋值
    在这里插入图片描述
    GC垃圾回收:
    程序计数器(PC)不需要GC,线程或者就一定要用,线程死了就一定没用了。
    :不需要GC负责。线程活的时候使用,线程消亡的时候消亡。
伪代码:
createThread(){
	创建线程对象;
	为这个对象分配资源;
	栈和pc内存;
	malloc(pc);
	malloc();
}
destoryThread(){
	free(pc);
	free();
}

总结:线程创建时创建,回收时回收。
方法区:类的加载和卸载过程。
需要类的时候就会加载/类肯定不需要了就去卸载。
类的占用空间相对于对象占用的比例要小很多,卸载的条件复杂。
GC是需要负责这里的。
:需要GC负责
GC回收内存的单位主要就是对象 ----以对象为单位进行垃圾回收
GC就是回收无用对象。
1,GC 是如何判断一个对象是无用的呢?
当一个对象没有引用指向的时候,可以认为这个对象就是没有用的对象。
引用计数器方案

  class JavaObject{
  int referenceCount=0;
  //对象的属性
  //对象的monitor lock
}
S s=new S();
JVM内部s对应的JavaObject.referenceCount++;
// s出了作用域之后
JVM内部s的对象的JavaObject.referenceCount--;
当referenceCount==0,表示该对象没有被引用,看做是没有用的对象。

引用计数方案会出现出的问题:循环引用
在这里插入图片描述
Java JVM比较先进,是不使用引用计数的方法。
下面分析可达性分析:
application 活着的过程,有一些引用(变量)是必须活着的
1.所有线程的栈帧中的局部变量(其中的引用)是必须活着的。
2.所有静态属性(其中的引用)是必须活着的。
这一系列引用被称为GC Roots
在这里插入图片描述
在这里插入图片描述
如何判断对象是否还活着:
1.看当前对象是否被引用指向-------引用计数法—JVM不使用,存在循环引用问题
2.通过GC Roots 名单 一路向下查找-------可达性分析 就是分析的过程耗时会有点长,因为需要遍历整颗树

(垃圾回收算法)对象的清理过程 释放内存的过程

在这里插入图片描述
既然没有一个通用的算法帮助我们做到快速,并且减少内存碎片。
呢我们想要追求,就可以进行垃圾分类。(和日常生活是一样的道理)

垃圾分代:

在这里插入图片描述
Stop The World 时候,我们需要停止应用,因为如果此时不停止,通过可达性分析,又分析出来的数据,就会被误杀掉,所以需要停止。
JVM:
1,程序是在JVM中如何执行的
ClassLoader
内存各个区域的作用
执行引擎的大概流程
2.GC
为什么要GC
GC 的单位是什么—对象
怎么判断一个对象活着:引用计数,可达性分析(JVM使用)
对象的漫游路径:
Eden ->Survior -> Old
在Young中是使用的复制算法
在Old中使用的是整理算法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值