Java学习之二(线程(了解) JVM GC 垃圾回收)
线程与进程(了解)→JVM→字节码→GC
一、程序 = 算法 + 数据结构(大佬)
二、程序 = 框架 + 业务逻辑(现实)
1.线程与进程、同步与异步
1.1进程是什么?
进程就是操作系统控制的基本运行单元,说白了就是Java运行程序。
1.2什么是线程?
进程中独立运行的子任务就是一个线程
1.3什么是多线程(异步)?
多线程是多个子任务进行交替执行。例如:有A、B两个任务,A先执行,再执行B,如果是单线程执行,则需要等A先执行完之后才能够执行B;而多线程的话是A与B来回交换执行。这样可以让系统的运行效率大大的提高。
多线程是异步的。
1.4同步与异步
同步:有两个程序:程序A与程序B,程序A调用程序B时,A等待B执行完成之后再执行。
异步:有两个程序:程序A与程序B,程序A调用程序B时,A不会等待B的执行,而是会自动的执行下去。
1.5线程共享与独享
线程共享:可优化,可回收
线程独享:不可优化,不可回收
2.JVM及内存调优
2.1JVM
JVM是Java Virtual Machine,是java虚拟机的缩写。JVM的本质上就是一段程序;JVM有5大模块:类装载子系统、运行时数据区、执行引擎、本地方法接口、垃圾回收机制。JVM实现了一次编译多次运行的特点,关键原因在于在编译成java字节码后,不管在什么平台上只要存在JVM虚拟机,就能够执行这个字节码,不用多次编译,这也是java跨平台的原因。
先来一张大致图:
2.2字节码
java字节码(.class),它是由java源文件(.java)通过java编译器编译而来的二进制流文件。是一种8位字节的文件。
2.3JVM的生命周期
在执行Java程序时,有几个程序执行就会有几个JVM(以下成为Java虚拟机)。Java虚拟机的只要还有程序执行就会一直存在,它的执行只能从main方法为起点进行执行,main方法称为初始线程。
Java中的线程分为两种:一、守护线程(一般是Java虚拟机自己使用的线程,当然也可以自己定义守护线程) 二、普通线程
在Java虚拟机中如果还存在普通线程,则Java虚拟机会一直执行。
2.4Java内存管理(参考上面的图)
类装载器(ClassLoader):负责加载.class文件。
虚拟机中有四种加载器,
JVM自带加载器:1.启动类加载器(bootstrap)(在jvm启动的时候就加载,使用的new就是在启动的时候自动加载);2.扩展类加载器(Extension);3.应用程序类加载器(AppClassLoader);
用户自己定义的加载器:1.继承java.lang.ClassLoader;
通过代码来进行查看,JVM自带的类加载器
public class demo01 {
public static void main(String[] args) {
demo01 demo01 = new demo01();
//通过反射获取类加载器的父类的父类
System.out.println(demo01.getClass().getClassLoader().getParent().getParent());
//通过反射获取类加载器的父类
System.out.println(demo01.getClass().getClassLoader().getParent());
//通过反射获取类加载器
System.out.println(demo01.getClass().getClassLoader());
}
}
运行结果为:
程序可得在进行类加载的时候,执行了应用程序类加载器、扩展程序类加载器,而启动加载器并未使用;这时候我们看jdk自带的Object类又是怎样的:
public class demo01 {
public static void main(String[] args) {
Object obj = new Object();
demo01 demo01 = new demo01();
System.out.println(obj.getClass().getClassLoader());
//通过反射获取类加载器的父类的父类
System.out.println(demo01.getClass().getClassLoader().getParent().getParent());
//通过反射获取类加载器的父类
System.out.println(demo01.getClass().getClassLoader().getParent());
//通过反射获取类加载器
System.out.println(demo01.getClass().getClassLoader());
}
}
运行结果
出现这种情况的原因,个人认为这是在JVM启动的时候就自动的执行了启动类加载器,并且加载了JDK中定义的类;
本地方法区与本地方法接口
native:调用底层第三方函数库(说明白一点就是调用C语言或者操作系统的函数库)
带有native关键字的方法全部进入本地方法栈,而不带native关键字的方法进入java栈;
程序计数器
每一个线程都有一个程序计数器,程序计数器记录了执行顺序。占用空间十分小,几乎可以忽略不计。
方法区/永久带(线程共享)
所有方法定义的信息都保存在方法区中,有:静态变量 + 常量 + 类信息(构造方法/接口信息) + 常量池
类的元数据(元数据并不是类的Class对象!Class对象是加载的最终产品,类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的)
栈
栈与队列
栈:后进先出,所以也装入方法,其中main方法一定实在栈底;
队列:先进先出
栈中存放数据:本地变量/局部变量(输入参数、输出参数、方法变量)、对象引用(使用指针进行对象的访问)、方法
堆
逻辑上:在JDK1.7之前,分为 新生区 -- 养老区 -- 永久区;在JDK1.8及以后,永久区被去除
一般来说,大多对象都是在新生区被回收;有部分对象在新生区中未被回收(轻回收,发生在伊甸园),则进入养老区(例如数据库连接池,重GC回收机制);如果养老区执行回收机制还是腾不出空间,则会出现OOM错误异常(俗称堆溢出)。
物理上: 新生区 --- 养老区
永久区(一般存放jar包)
jDK 1.6 有永久带,常量池在方法区
JDK 1.7有永久带,永久代存在于虚拟机中,常量池在堆
JDK 1.8 无永久带,常量池在元空间,元空间并不在虚拟机中,而是存在于本地内存;
堆调优
通过代码,查看虚拟机内存相关情况
public class Demo01 {
public static void main(String[] args) {
//虚拟机试图使用的最大内存
long max01 = Runtime.getRuntime().maxMemory();
//虚拟机内存的总量
long max02 = Runtime.getRuntime().totalMemory();
System.out.println("" + max01/1024/1024 + "M");
System.out.println("" + max02/1024/1024 + "M");
}
}
运行结果如下
1801M
123M Process finished with exit code 0
简单介绍
-Xms 设置初始分配大小,默认为物理内存的 1/64;
-Xmx 设置最大分配内存,默认为物理内存的 1/4;
-XX:+PrintGCDetails 打印GC回收信息
上面的我物理内存大小为 8G,计算结果也大致相似。
JVM参数调优设置(IDEA):
我这里将最大内存与初始内存设置为100M
运行结果为:
96M
96M
Heap
PSYoungGen total 29696K, used 2584K [0x00000000fdf00000, 0x0000000100000000, 0x0000000100000000)
eden space 25600K, 10% used [0x00000000fdf00000,0x00000000fe1861c0,0x00000000ff800000)
from space 4096K, 0% used [0x00000000ffc00000,0x00000000ffc00000,0x0000000100000000)
to space 4096K, 0% used [0x00000000ff800000,0x00000000ff800000,0x00000000ffc00000)
ParOldGen total 68608K, used 0K [0x00000000f9c00000, 0x00000000fdf00000, 0x00000000fdf00000)
object space 68608K, 0% used [0x00000000f9c00000,0x00000000f9c00000,0x00000000fdf00000)
Metaspace used 3144K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 343K, capacity 388K, committed 512K, reserved 1048576K Process finished with exit code 0
设置成功!
测试堆溢出
这里我将大小设置为1M,代码如下
public class Demo02 {
public static void main(String[] args) {
//虚拟机试图使用的最大内存
long max01 = Runtime.getRuntime().maxMemory();
//虚拟机内存的总量
long max02 = Runtime.getRuntime().totalMemory();
System.out.println("" + max01/1024/1024 + "M");
System.out.println("" + max02/1024/1024 + "M");
String a = "abc";
while (true){
a = a + new Random().nextInt(123456);
}
}
}
成功堆溢出,出现OOM错误
[Full GC (Ergonomics) [PSYoungGen: 512K->477K(1024K)] [ParOldGen: 511K->477K(512K)] 1023K->955K(1536K), [Metaspace: 3738K->3738K(1056768K)], 0.0141204 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Arrays.java:3664)
at java.lang.String.<init>(String.java:207)
at java.lang.StringBuilder.toString(StringBuilder.java:407)
at org.study.heapstudy.Demo02.main(Demo02.java:15)
关于内存调优尚有不懂,以后补充。这里先留一个,内存分析工具:JProFiler
3.Java垃圾回收简单概述
1.1为什么会存在垃圾回收机制?
如果不进行垃圾回收,则会造成资源的占用,持续下去内存的话,内存迟早要被消耗一空;所以引入垃圾回收机制,在恰当的时机进行垃圾的回收,释放掉一些内存。相对与C来说,Java的垃圾回收机制是自动的,并不需要手动的去回收垃圾。
1.2如何理解垃圾回收机制?
前面说到堆有几个时期, 新生区 --- 养老区 --- 永久区(理论存在,物理上并不在JVM中,而是元空间中也就是物理内存中)。当养老区满了却又无法释放的时候,则会造成堆溢出。同理新生区如果没有垃圾回收的话,也会造成堆溢出;
1.3 哪些对象不会被回收?
栈、本地方法栈、方法区。
1.4 GC清除算法?
1.4.1 标记清理算法 缺点:会产生内存碎片
1.4.2 标记整理算法 优点:减少内存碎片 缺点:对象迁移,代价大
1.4.3 复制算法 缺点:需要两倍的内存
新生代(伊甸园(存活的进入一区,没用的就直接被回收)、一区、二区) 养老带(存放年龄 >= 6,或者存放大对象);注意一区、二区交替工作 比例为 1:1:8
小结
首先是JVM
JVM有5大模块:类加载器 --- 运行时数据区 --- 执行引擎 --- 本地方法接口 --- 垃圾回收机制
类加载器(ClassLoader):3种JVM自带(bootstrap 启动类加载器、Extension 扩展类加载器、AppClassLoader 应用程序类加载器) + 1种自己定义的类加载器 (继承java.lang.ClassLoader)
运行时数据区:java栈、虚拟方法栈、堆、程序计数器、方法区
java栈:存放基本数据类型、类引用、局部变量(局部变量,在方法中的大多都是局部变量,所以姑且可以认为方法是存在于栈中)
虚拟方法栈:存放native修饰的方法,native所修饰的方法代表引用C/C++代码或者操作系统代码
方法区:方法区存放静态变量、常量,网上太多的人也是说不清楚,这里姑且认为存放static修饰的静态变量、常量、.class中的信息。
堆:存放new的对象信息
程序计数器:指定程序执行的顺序
永久代:前面可以理解为方法区,但是在JDK1.8以后就取消了永久代的概念,变成了元空间(所谓的元空间也就是 物理内存)
本地方法接口:用native申明的方法,调用其它语言(C/C++)实现的方法,保存在本地方法栈中
GC垃圾回收机制:在堆中分为 新生代 --- 养老代 --- 永久代(逻辑上存在,物理内存上并不存在)
新生代分为(伊甸园、新生代1、新生代2),刚刚new出来的对象存放于伊甸园;而后没有被回收的对象存放在新生代1中,新生代1和新生代2是交替使用的;如果在多轮垃圾回收之后,新生代中的对象都没有被回收的话,则会进入养老代 ;在养老代中的对象过多且无法及时回收,使得内存溢出,就会造成堆溢出,从而促使抛出OOM异常。
在引用之中,分为强引用、弱引用、软引用、虚引用
强引用(new)出来的对象,这个等级的对象在没有引用的时候才会被GC回收,内存不够的情况下也不会回收这种类型的对象;
弱引用:在内存不够的情况下,会回收这种类型的对象;通常运用于浏览器缓存
软引用:随时可能会被回收
虚引用:GC执行的时候会被直接回收;这个性质可以被用来监听GC什么时候执行
JVM内存优化
了解不多,
-Xms设置初始值大小 默认值为 物理内存 的 1/64
-Xmx设置内存的最大大小 默认值为 物理内存 的 1/4
想要知道程序性能,可以使用JProFilter来进行内存监控。
Java学习之二(线程(了解) JVM GC 垃圾回收)的更多相关文章
- Java 中级 学习笔记 2 JVM GC 垃圾回收与算法
前言 在上一节的学习中,已经了解到了关于JVM 内存相关的内容,比如JVM 内存的划分,以及JDK8当中对于元空间的定义,最后就是字符串常量池等基本概念以及容易混淆的内容,我们都已经做过一次总结了.不 ...
- RPC调用与GC垃圾回收
RPC调用 多个服务协同完成一次业务时,由于业务约束(如红包不符合使用条件.账户余额不足等).系统故障(如网络或系统超时或中断.数据库约束不满足等),都可能造成服务处理过程在任何一步无法继续,使数据处 ...
- Java虚拟机笔记(二):GC垃圾回收和对象的引用
为什么要了解GC 我们都知道Java开发者在开发过程中是不需要关心对象的回收的,因为Java虚拟机的原因,它会自动回收那些失效的垃圾对象.那我们为什么还要去了解GC和内存分配呢? 答案很简单:当我们需 ...
- java面试题之----JVM架构和GC垃圾回收机制详解
JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...
- JVM学习02:GC垃圾回收和内存分配
JVM学习02:GC垃圾回收和内存分配 写在前面:本系列分享主要参考资料是 周志明老师的<深入理解Java虚拟机>第二版. GC垃圾回收和内存分配知识要点Xmind梳理 案例分析1-(G ...
- java学习笔记15--多线程编程基础2
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...
- Java 类加载机制 ClassLoader Class.forName 内存管理 垃圾回收GC
[转载] :http://my.oschina.net/rouchongzi/blog/171046 Java之类加载机制 类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指 ...
- JVM架构和GC垃圾回收机制详解
JVM架构图分析 下图:参考网络+书籍,如有侵权请见谅 (想了解Hadoop内存溢出请看:Hadoop内存溢出(OOM)分类.参数调优化) JVM被分为三个主要的子系统 (1)类加载器子系统(2)运行 ...
- 面试官,不要再问我“Java GC垃圾回收机制”了
Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解> ...
随机推荐
- Ubuntu 最简单的方式安装chrome
1.指定安装目录如下: cd opt/ 2.下载包: sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current ...
- 【笔记篇】不普及向——莫比乌斯反演学习笔记 && 栗题HAOI2011 Problem B
Part0 广告(当然没有广告费) P.S. 这篇文章是边学着边用Typora写的...学完了题A了blog也就呼之欲出了~有latex化式子也非常方便...非常建议喜欢Markdown的dalao们 ...
- JS随机产生颜色
<script> function selectForm(lowerValue,upperValue){ var choices=upperValue-lowerValue+1; retu ...
- LUOGU P3919 【模板】可持久化数组(主席树)
传送门 解题思路 给每一时刻建一棵线段树维护当前时刻的值,然后修改的时候直接修改,查询的时候直接查,记住查询完后一定要复制. 代码 #include<iostream> #include& ...
- Codeforces-GYM101873 G Water Testing 皮克定理
题意: 给定一个多边形,这个多边形的点都在格点上,问你这个多边形里面包含了几个格点. 题解: 对于格点多边形有一个非常有趣的定理: 多边形的面积S,内部的格点数a和边界上的格点数b,满足如下结论: 2 ...
- android GPS: code should explicitly check to see if permission is available
转载的,感谢作者,由于我找了很久才找到这个解决方法,因此我自己再转一遍 原文链接 https://blog.csdn.net/qinwendou/article/details/77849048 if ...
- 在ubuntu下编写python
一般情况下,ubuntu已经安装了python,打开终端,直接输入python,即可进行python编写. 默认为python2 如果想写python3,在终端输入python3即可. 如果需要执行大 ...
- HDU-1492-The number of divisors(约数) about Humble Numbers -求因子总数+唯一分解定理的变形
A number whose only prime factors are 2,3,5 or 7 is called a humble number. The sequence 1, 2, 3, 4, ...
- 9个搜索引擎优化(SEO)最佳实践
作为网页设计师,搜索引擎优化重要吗?我们知道,网站设计是把屏幕上平淡无奇变成令人愉快的美感,更直观地辨认信息.这也是人与人之间在沟通想法,这样的方式一直在演变. 1. 网站结构 对于搜索引擎优化,网站 ...
- C++返回引用的需求
1.重载+=操作符返回*this或者某个参数的引用可以方便链式调用,比如C++流操作就是cout<< a << b << c这样的,就是靠不停返回stream的引用 ...