发表于: 2018-02-07 23:04:12
1 785
今日完成
1.java内存分配学习;
方法区:又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
今天查看资料发量池存放在堆中还是存放在方法区中有不同的说法
----运行时常量池都分配在 Java 虚拟机的方法区之中;-----
这和昨天查到的说法:------常量池存放于堆中-----不同
明天查资料验证;
查到资料中提到:方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
Java的GC((Garbage Collection)机制:垃圾回收机制:
Java的内存回收主要在Java的堆上进行的。
常用垃圾收集算法
1. 标记-清除算法
先标记需要所有回收的对象,然后回收所有需要回收的对象;
缺点,造成内存块在回收之后没有连续性,当需要一大块的内存时,系统无法进行分配;
2. 标记-清除-压缩
优化了上一种算法的,在清除之后对内存进行了压缩,保证了内存块的连续性,但是由于这种压缩是通过不停的内存间的拷贝和复制进行的,所以性能会非常差;
3.标记-清除-复制
这种算法会将内存空间分配成两块相同的区域1和2。当内存回收的时候,将2中的内存块拷贝到1中,然后一次性清空1。
java的分代算法:
JVM区域总体分两类,heap区和非heap区。heap区又分:Eden Space(伊甸园)、Survivor Space(幸存者区)、Tenured Gen(老年代-养老区)。 非heap区又分:Code Cache(代码缓存区)、Perm Gen(永久代)、Jvm Stack(java虚拟机栈)、Local Method Statck(本地方法栈)。
a.新生代(young generation)和老生代(Old Generation);
Java的新生代中,对象的存活率低,存活期期会相对会比较短一些,所以可以选用复制算法来进行内存回收。
Java的老生代中,对象的存活率比较高,并且相对存活期比较长一些,可以采用标记-清除-压缩的算法来进行内存回收。
其中,通常新生代分为Eden Space(伊甸园)和2个Survivor Space(幸存者区)
Java的方法区间和常量池我们一般称为永久代(在JDK8中永生代已经完全消失,,转而使用元空间。而元空间是直接存在内存中,不在java虚拟机中的,因此元空间依赖于内存大小。)
Java的对象会优先在Eden Space(伊甸园)上分配,这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。(从新生代(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到老年代中。)
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区(上图中Survivor2区),Survivor区“To”(上图中Sruvivor2区)是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)来决定去向。没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”(上图中Surviror2区)就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
2.多线程学习;
join方法、yield方法的学习和练习;
public class Runnabletest1 implements Runnable {
private String name;
private int s=10;
public Runnabletest1(String name) {
this.name = name;
}
//重新run方法
public void run() {
System.out.println("线程运行开始");
for (int i = 0; i < 5; i++) {
s=s-1;
System.out.println(name+" 子线程运行 "+s);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+" thread complated");
}
public static void main(String[] args) {
System.out.println("mainThread start");
Runnabletest1 A1=new Runnabletest1("a1");
Runnabletest1 A2=new Runnabletest1("a2");
Thread a1=new Thread(A1);
Thread a2=new Thread(A2);
a1.start();
a2.start();
try {
a1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
a2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("mainThread completed");
}
}
public class YieldTest implements Runnable {
private String name;
private Thread thread;
public YieldTest(String name) {
this.name = name;
}
public void run() {
System.out.println(name+"子线程开始运行");
synchronized(""){
for(int i=0;i<10;i++) {
System.out.println(name + " 线程运行 " + i);
if(i == 30){
new Thread().yield();
//问题在这里
}
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}}
}
System.out.println(name+" 子线程结束");
}
public static void main(String[] args) {
System.out.println("主程序开始");
YieldTest A1=new YieldTest("a1");
YieldTest A2=new YieldTest("a2");
Thread a1=new Thread(A1);
Thread a2=new Thread(A2);
a1.start();
a2.start();
try {
a1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
a2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束");
}
}
public class Threadtest1 extends Thread {
private String name;
public Threadtest1(String name) {
super(name);
this.name = name;
}
public void run() {//重写run方法
for(int i=0;i<50;i++) {
System.out.println(name + i);
if(i == 30){
this.yield();
}
}
}
public static void main(String[] args) {
//实例化对象
Threadtest1 a=new Threadtest1("A");
Threadtest1 b=new Threadtest1("B") ;
//调用start方法启动线程
a.start();
b.start();
}
}
明天计划
1.学习多线程;
synchronized、wait、notify等方法的运用;
收获
学习了java的内存分配和GC机制,写代码时思路更为清晰;
学习了多线程的一些知识点;
遇到问题
上面第二段代码中,使用this.yield()会报错,这里自己分析了下:这里实现了Runnable接口,但是Runnable接口也是通过Thread类来实现多线程。这里的run()方法与Thread类中的方法相同,但是Runnable接口中没有yield方法,所以重写中需要实例化Thread类的对象。虽然这里没有再报错,但是还是有点想不通,感觉上面的代码并不能明确体现出yield方法的作用;
评论