程序的栈和堆
栈:每个线程一个
main{
Object o = new Object();
m();
}
main主函数进栈,新建Object对象,o指针指向堆中的new出来的Object对象空间
栈空间自动释放内存,但是堆空间不能主动释放,所以需要标记空间可用来进行释放这是主要的思想(这是程序之中最难调试的bug俗称野指针)
野指针:同一个对象,一个指针释放了,但是另一个指针还不知道,还在使用
同一指针,不同位置
不在指向任何地址的指针
NullPointerException
并发问题:
多线程访问同一个内存空间
语言的发展历史
C/C++ -手工管理 malloc free/newdelete 进行手工释放
问题:忘记释放 -memory leak内存泄漏 --泄漏过多导致内存减少导致内存溢出
所以衍生出调优常用手法:重启
释放多次 --其他线程将你的内存空间释放(产生极其难以调试的bug)
开发效率极低
java python go
-方便内存管理的语言
-GC -Garbage Collector垃圾回收器--应用线程负责分配,垃圾回收器负责回收
-开发效率非常高大大的降低程序员的门槛
问题:相比于C/C++执行效率变低,因为垃圾回收器开始干活时,所有线程停止
rust
-运行效率超高 (asm c c++)
-不用手工管理内存(没有GC)
-学习曲线超高(ownership)
-一夫一妻制:同一时间一个内存空间只有一个指针指向-出栈的时候指针指向的空间一起回收
只要语法通过,就写不出bug(多线程访问同一个空间只能通过特定的写法)
JVM的GC的历史
怎么定义垃圾:
引用计数法:python --问题:循环引用
根可达算法:顺着根找找到的都不是垃圾
垃圾清除算法:(截止目前只有三种)
Mark-Sweep(标记清除):标记后垃圾回收区进行清除 --问题:碎片化内存
Copying(拷贝)标记之后集体复制到另一边,整体回收另一边--缺点浪费内存
Mark-Compact(标记压缩)标记的时候进行整理--缺点效率比copying低
垃圾回收器就是综合运用这三种垃圾回收算法,诞生了各种各样的垃圾回收器
垃圾回收器(十种)
GC的演化
随着内存的不断增长而不断演变
几兆到几十兆 Serial(copying)-Serial Old(标记清除和标记压缩) 单线程STW垃圾回收 年轻代,老年代都一样
几十兆到上百兆1g(单线程不好使) parallel(并行ps+po)多线程STW垃圾回收 年轻代,老年代都一样
几十G Concurrent GC GC线程和业务线程同时运行
CMS(concurrent Mark Sweep)
初始标记:从根上开始找(STW不过时间很短因为只找根对象)
并发标记:并发标记为垃圾(肯定会导致错标,因为并发会导致引用没了又引入回来) 三色标记算法:
问题更正:incremental update
重新标记:重新标记(也是STW不过因为标错的不会特别多所以很快)
ParNew 主要是配合CMS使用
Epsilon(什么都不干)1、主要是给开发JVM垃圾回收器的人Debug
2、假如内存超级大并且知道程序运行完根本不需要这么大内存,所以效率非常高
G1(分区回收)
毛病:一次回收YGC整个年轻代,物理不分代逻辑分代
ZGC(分页算法、GoLang的核心)
没有分代
堆内存逻辑分区
新生代(copying算法):一次回收掉90%,所以一般只会复制一成,活着的放到survivor(幸存区1),下一次在deen和s1一起回收都放在s2,在下一次放在s1依次循环,survivor装不下了放老年代
老年代(三种算法综合使用):经历多次垃圾回收器的回收都没有扫掉,不扫了(内存不足的时候在扫)
年轻代满了产生YGC,老年代满了产生FGC