HeLei Blog

不要因为走得太远,就忘记为什么而出发


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索
close
HeLei Blog

java基础(一)-编译时类型和运行时类型

发表于 2017-10-09 | 分类于 Java

Java的引用变量有两个类型,一个是编译时类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,会出现所谓的多态。因为子类其实是一种特殊的父类,因此java允许把一个子类对象直接赋值给一个父类引用变量,无须任何类型转换,或者被称为向上转型,由系统自动完成。

class Base
{
    int i = 1;
    Base() {
        System.out.println(this.i);
        System.out.println(this.getClass());
        this.print();
    }

    void print() {
        System.out.println("Base print:"+i);
    }
}

class Derived extends Base
{
    int i = 2;
    void print() {
        System.out.println("Derived print:"+i);
    }
}

public class Test02 {
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}

运行结果

1(Base.i)
class com.java_basic.test01.Derived
Derived print:0(Derived.print)

Java这里与c++不同之处是,c++的运行时多态依赖virtual关键字,c++中若在Base构造函数中调用print方法,是调用Base的print方法。有这样一条规则,对象调用编译时类型的属性和运行时类型的方法。

HeLei Blog

atomic variable

发表于 2017-09-26 | 分类于 Linux

##How atomic variables work

Intel的x86和x86_64架构当访问内存时,有锁住前端总线(FSB)的指令,前端总线是处理器和RAM通信的通道,锁住FSB可以阻止其它处理器或进程在当前处理器运行,也可以阻止访问RAM。

##原子变量的字节数限制
Intel的专家建议并不用每次访问内存都要锁住FSB,Intel处理器允许memcpy() and memcmp()允许在一个处理器上执行,但锁住大块内存的代价太大。实际使用中,当访问1, 2, 4, 8字节的整数时可以锁住FSB,gcc支持int、long、long long的原子操作。

##什么情况下不能使用原子变量

1
2
3
decrement_atomic_value();
if (atomic_value() == 0)
fire_a_gun();

##gcc支持的原子操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
type __sync_fetch_and_add (type *ptr, type value);
type __sync_fetch_and_sub (type *ptr, type value);
type __sync_fetch_and_or (type *ptr, type value);
type __sync_fetch_and_and (type *ptr, type value);
type __sync_fetch_and_xor (type *ptr, type value);
type __sync_fetch_and_nand (type *ptr, type value);
type __sync_add_and_fetch (type *ptr, type value);
type __sync_sub_and_fetch (type *ptr, type value);
type __sync_or_and_fetch (type *ptr, type value);
type __sync_and_and_fetch (type *ptr, type value);
type __sync_xor_and_fetch (type *ptr, type value);
type __sync_nand_and_fetch (type *ptr, type value);

使用原子操作的例子

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <iostream>

#define INC_TO 1000000 // one million...

int global_int = 0;

pid_t gettid( void )
{
    return syscall( __NR_gettid );
}

void *thread_routine( void *arg )
{
    int i;
    int proc_num = (int)(long)arg;
    cpu_set_t set;

    CPU_ZERO( &set );
    CPU_SET( proc_num, &set );

    if (sched_setaffinity( gettid(), sizeof( cpu_set_t ), &set ))
    {
        perror( "sched_setaffinity" );
        return NULL;
    }

    for (i = 0; i < INC_TO; i++)
    {
        global_int++;
        //__sync_fetch_and_add( &global_int, 1 );
    }

    return NULL;
}

int main()
{
    int procs = 0;
    int i;
    pthread_t *thrs;

    // Getting number of CPUs
    procs = (int)sysconf( _SC_NPROCESSORS_ONLN );
    if (procs < 0)
    {
        perror( "sysconf" );
        return -1;
    }
    else
    {
        std::cout << "cpu nums:" << procs << std::endl;
    }

    thrs = (pthread_t*)malloc( sizeof( pthread_t ) * procs );
    if (thrs == NULL)
    {
        perror( "malloc" );
        return -1;
    }

    printf( "Starting %d threads...\n", procs );

    for (i = 0; i < procs; i++)
    {
        if (pthread_create( &thrs[i], NULL, thread_routine,
            (void *)(long)i ))
        {
            perror( "pthread_create" );
            procs = i;
            break;
        }
    }

    for (i = 0; i < procs; i++)
        pthread_join( thrs[i], NULL );

    free( thrs );

    printf( "After doing all the math, global_int value is: %d\n",
        global_int );
    printf( "Expected value is: %d\n", INC_TO * procs );

    return 0;
}

平均耗时90ms

同样的逻辑使用互斥锁的例子

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <iostream>
#include <unistd.h>  
#include <sys/types.h> 
#include <time.h>
#include <sys/time.h>

#define INC_TO 1000000 // one million...

int global_int = 0;
pthread_mutex_t mutex;

pid_t gettid( void )
{
    return syscall( __NR_gettid );
}

uint64_t get_time()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec*1000 + tv.tv_usec/1000;
}

void *thread_routine( void *arg )
{
    int i;
    int proc_num = (int)(long)arg;
    cpu_set_t set;

    CPU_ZERO( &set );
    CPU_SET( proc_num, &set );

    if (sched_setaffinity( gettid(), sizeof( cpu_set_t ), &set ))
    {
        perror( "sched_setaffinity" );
        return NULL;
    }

    for (i = 0; i < INC_TO; i++)
    {
        pthread_mutex_lock(&mutex);
        global_int++;
        pthread_mutex_unlock(&mutex);
    }

    return NULL;
}

int main()
{
    int procs = 0;
    int i;
    pthread_t *thrs;

    // Getting number of CPUs
    procs = (int)sysconf( _SC_NPROCESSORS_ONLN );
    if (procs < 0)
    {
        perror( "sysconf" );
        return -1;
    }
    else
    {
        std::cout << "cpu nums:" << procs << std::endl;
    }

    thrs = (pthread_t*)malloc( sizeof( pthread_t ) * procs );
    if (thrs == NULL)
    {
        perror( "malloc" );
        return -1;
    }

    printf( "Starting %d threads...\n", procs );
    uint64_t begin_ts = get_time();
    pthread_mutex_init(&mutex, NULL);

    for (i = 0; i < procs; i++)
    {
        if (pthread_create( &thrs[i], NULL, thread_routine,
            (void *)(long)i ))
        {
            perror( "pthread_create" );
            procs = i;
            break;
        }
    }

    for (i = 0; i < procs; i++)
        pthread_join( thrs[i], NULL );

    pthread_mutex_destroy(&mutex);
    uint64_t end_ts = get_time();

    printf("time costs:%lld\n", (long long int)end_ts-begin_ts);
    free( thrs );

    printf( "After doing all the math, global_int value is: %d\n",
        global_int );
    printf( "Expected value is: %d\n", INC_TO * procs );

    return 0;
}

平均耗时400ms,可以看出原子操作相比互斥锁有很大的性能优势.

参考文献

http://www.alexonlinux.com/multithreaded-simple-data-type-access-and-atomic-variables#table_of_contents

HeLei Blog

linux-memory-barrier

发表于 2017-09-05
HeLei Blog

java-timer

发表于 2017-08-21 | 分类于 Java

使用Timer类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class TimerTest {
public static String getStringDate() {
Date currentTime = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = formatter.format(currentTime);
return dateString;
}
public static void main(String[] args) throws InterruptedException {
final TimerTask timerTask1 = new TimerTask() {
@Override
public void run() {
try {
System.out.println("task2 invoked!"+Thread.currentThread().getId());
System.out.println("begin: "+getStringDate());
//Thread.sleep(5000L);
} catch (Exception e) {
e.printStackTrace();
}
}
};
final TimerTask timerTask2 = new TimerTask() {
@Override
public void run() {
try {
System.out.println("task2 invoked!"+Thread.currentThread().getId());
System.out.println("begin: "+getStringDate());
//Thread.sleep(5000L);
} catch (Exception e) {
e.printStackTrace();
}
}
};
Timer timer = new Timer();// 实例化Timer
timer.schedule(timerTask1, 8000);
timer.schedule(timerTask2, 10000);
}
}

Timer

1
2

HeLei Blog

Protocal Buffer trap 1-CopyFrom

发表于 2017-06-22 | 分类于 C++

最近在使用pb时遇到了这么一个问题,还是废了不少时间去定位,出现coredump的场景类似下面的代码

1
2
3
4
Student stu;
Teacher teacher;
stu.CopyFrom(teacher);

奇怪的是这样居然可以编译通过,查了pb的源码发现类都是共同的基类Message,而有两个overload版本

1
2
void CopyFrom(const ::google::protobuf::Message& from);
void CopyFrom(const Student& from);

终于恍然大悟了!!

HeLei Blog

java-cmd01-jps

发表于 2017-05-18 | 分类于 Java

位置

我们知道,很多Java命令都在jdk的JAVA_HOME/bin目录下面,jps也不例外,他就在bin目录下,所以,他是java自带的一个命令。

功能

jps(Java Virtual Machine Process Status Tool)是JDK1.5提供的一个显示当前所有java进程pid的命令,简单实用,非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。

原理

jdk中的jps命令可以显示当前运行的java进程以及相关参数,它的实现机制如下:
java程序在启动以后,会在java.io.tmpdir指定的目录下,就是临时文件夹里,生成一个类似于hsperfdataUser的文件夹,这个文件夹里(在Linux中为/tmp/hsperfdata{userName}/),有几个文件,名字就是java进程的pid,因此列出当前运行的java进程,只是把这个目录里的文件名列一下而已。至于系统的参数什么,就可以解析这几个文件获得。

使用

-q 只显示pid,不显示class名称,jar文件名和传递给main 方法的参数
-m 输出传递给main 方法的参数,在嵌入式jvm上可能是null
-l 输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名
-v 输出传递给JVM的参数

PS:jps命令有个地方很不好,似乎只能显示当前用户的java进程,要显示其他用户的还是只能用unix/linux的ps命令

HeLei Blog

java-cmd01-jstat

发表于 2017-05-18 | 分类于 Java

jstat位于java的bin目录下,主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。可见,Jstat是轻量级的、专门针对JVM的工具,非常适用。

jstat 命令格式

1
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

参数解释:
Option — 选项,我们一般使用 -gcutil 查看gc情况
vmid — VM的进程号,即当前运行的java进程号
interval– 间隔时间,单位为秒或者毫秒
count — 打印次数,如果缺省则打印无数次

参数interval和count代表查询间隔和次数,如果省略这两个参数,说明只查询一次。假设需要每250毫秒查询一次进程5828垃圾收集状况,一共查询5次,那命令行如下:

1
jstat -gc 5828 250 5

option

选项option代表这用户希望查询的虚拟机信息,主要分为3类:类装载、垃圾收集和运行期编译状况,具体选项及作用如下:

param description
–class 监视类装载、卸载数量、总空间及类装载所耗费的时间
–gc 监视Java堆状况,包括Eden区、2个Survivor区、老年代、永久代等的容量
–gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大和最小空间
–gcutil 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
–gccause 与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因
–gcnew 监视新生代GC的状况
–gcnewcapacity 监视内容与-gcnew基本相同,输出主要关注使用到的最大和最小空间
–gcold 监视老年代GC的状况
–gcoldcapacity 监视内容与——gcold基本相同,输出主要关注使用到的最大和最小空间
–gcpermcapacity 输出永久代使用到的最大和最小空间
-compiler 输出JIT编译器编译过的方法、耗时等信息
–printcompilation 输出已经被JIT编译的方法

1、jstat –class : 显示加载class的数量,及所占空间等信息。
Loaded 装载的类的数量
Bytes 装载类所占用的字节数
Unloaded 卸载类的数量
Bytes 卸载类的字节数
Time 装载和卸载类所花费的时间

2、jstat -compiler : 显示VM实时编译的数量等信息。

Compiled 编译任务执行数量 Failed 编译任务执行失败数量 Invalid 编译任务执行失效数量 Time 编译任务消耗时间 FailedType 最后一个编译失败任务的类型 FailedMethod 最后一个编译失败任务所在的类及方法

3、jstat -gc : 可以显示gc的信息,查看gc的次数,及时间。

S0C 年轻代中第一个survivor(幸存区)的容量 (字节) S1C 年轻代中第二个survivor(幸存区)的容量 (字节) S0U 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) S1U 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) EC 年轻代中Eden(伊甸园)的容量 (字节) EU 年轻代中Eden(伊甸园)目前已使用空间 (字节) OC Old代的容量 (字节) OU Old代目前已使用空间 (字节) PC Perm(持久代)的容量 (字节) PU Perm(持久代)目前已使用空间 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 YGCT 从应用程序启动到采样时年轻代中gc所用时间(s) FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

4、jstat -gccapacity :可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小

NGCMN 年轻代(young)中初始化(最小)的大小(字节) NGCMX 年轻代(young)的最大容量 (字节) NGC 年轻代(young)中当前的容量 (字节) S0C 年轻代中第一个survivor(幸存区)的容量 (字节) S1C 年轻代中第二个survivor(幸存区)的容量 (字节) EC 年轻代中Eden(伊甸园)的容量 (字节) OGCMN old代中初始化(最小)的大小 (字节) OGCMX old代的最大容量(字节) OGC old代当前新生成的容量 (字节) OC Old代的容量 (字节) PGCMN perm代中初始化(最小)的大小 (字节) PGCMX perm代的最大容量 (字节)
PGC perm代当前新生成的容量 (字节) PC Perm(持久代)的容量 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数

5、jstat -gcutil :统计gc信息

S0 年轻代中第一个survivor(幸存区)已使用的占当前容量百分比 S1 年轻代中第二个survivor(幸存区)已使用的占当前容量百分比 E 年轻代中Eden(伊甸园)已使用的占当前容量百分比 O old代已使用的占当前容量百分比 P perm代已使用的占当前容量百分比 YGC 从应用程序启动到采样时年轻代中gc次数 YGCT 从应用程序启动到采样时年轻代中gc所用时间(s) FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

6、jstat -gcnew :年轻代对象的信息。

S0C 年轻代中第一个survivor(幸存区)的容量 (字节) S1C 年轻代中第二个survivor(幸存区)的容量 (字节) S0U 年轻代中第一个survivor(幸存区)目前已使用空间 (字节) S1U 年轻代中第二个survivor(幸存区)目前已使用空间 (字节) TT 持有次数限制 MTT 最大持有次数限制 EC 年轻代中Eden(伊甸园)的容量 (字节) EU 年轻代中Eden(伊甸园)目前已使用空间 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 YGCT 从应用程序启动到采样时年轻代中gc所用时间(s)

7、jstat -gcnewcapacity: 年轻代对象的信息及其占用量。

NGCMN 年轻代(young)中初始化(最小)的大小(字节) NGCMX 年轻代(young)的最大容量 (字节) NGC 年轻代(young)中当前的容量 (字节) S0CMX 年轻代中第一个survivor(幸存区)的最大容量 (字节) S0C 年轻代中第一个survivor(幸存区)的容量 (字节) S1CMX 年轻代中第二个survivor(幸存区)的最大容量 (字节) S1C 年轻代中第二个survivor(幸存区)的容量 (字节) ECMX 年轻代中Eden(伊甸园)的最大容量 (字节) EC 年轻代中Eden(伊甸园)的容量 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数

8、jstat -gcold :old代对象的信息。

PC Perm(持久代)的容量 (字节) PU Perm(持久代)目前已使用空间 (字节) OC Old代的容量 (字节) OU Old代目前已使用空间 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

9、stat -gcoldcapacity : old代对象的信息及其占用量。

OGCMN old代中初始化(最小)的大小 (字节) OGCMX old代的最大容量(字节) OGC old代当前新生成的容量 (字节) OC Old代的容量 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

10、jstat -gcpermcapacity: perm对象的信息及其占用量。

PGCMN perm代中初始化(最小)的大小 (字节) PGCMX perm代的最大容量 (字节)
PGC perm代当前新生成的容量 (字节) PC Perm(持久代)的容量 (字节) YGC 从应用程序启动到采样时年轻代中gc次数 FGC 从应用程序启动到采样时old代(全gc)gc次数 FGCT 从应用程序启动到采样时old代(全gc)gc所用时间(s) GCT 从应用程序启动到采样时gc用的总时间(s)

11、jstat -printcompilation :当前VM执行的信息。

Compiled 编译任务的数目 Size 方法生成的字节码的大小 Type 编译类型 Method 类名和方法名用来标识编译的方法。类名使用/做为一个命名空间分隔符。方法名是给定类中的方法。上述格式是由-XX:+PrintComplation选项进行设置的

HeLei Blog

GC algorithm

发表于 2017-05-12 | 分类于 Java

##1 按照基本回收策略分
标记-清除算法
算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,它的标记过程其实在前一节讲述对象标记判定时已经介绍过了。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。
它的主要不足有两个:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法
为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。 当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。 只是这种算法的代价是将内存缩小为了原来的一半,未免太高了一点。

分代收集算法
当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。 而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

分代垃圾回收详解:
分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。

在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中的Session对象、线程、Socket连接,这类对象跟业务直接挂钩,因此生命周期比较长。但是还有一些对象,主要是程序运行过程中生成的临时变量,这些对象生命周期会比较短,比如:String对象,由于其不变类的特性,系统会产生大量的这些对象,有些对象甚至只用一次即可回收。

试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,花费时间相对会长,同时,因为每次回收都需要遍历所有存活对象,但实际上,对于生命周期长的对象而言,这种遍历是没有效果的,因为可能进行了很多次遍历,但是他们依旧存在。因此,分代垃圾回收采用分治的思想,进行代的划分,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收。

虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。

年轻代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

年老代:
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

持久代:
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。

什么情况下触发垃圾回收
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。

Scavenge GC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用
·上一次GC之后Heap的各域分配策略动态变化

##2 按分区对待的方式分
增量收集(IncrementalCollecting):实时垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。

分代收集(Generational Collecting):基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。

##3 按系统线程分
串行收集:串行收集使用单线程处理所有垃圾回收工作,因为无需多线程交互,实现容易,而且效率比较高。但是,其局限性也比较明显,即无法使用多处理器的优势,所以此收集适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。

并行收集:并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU数目越多,越能体现出并行收集器的优势。(串型收集的并发版本,需要暂停jvm)并行paralise指的是多个任务在多个cpu中一起并行执行,最后将结果合并。效率是N倍。

并发收集:相对于串行收集和并行收集而言,前面两个在进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行,因此,系统在垃圾回收时会有明显的暂停,而且暂停时间会因为堆越大而越长。(和并行收集不同,并发只有在开头和结尾会暂停jvm)并发concurrent指的是多个任务在一个cpu伪同步执行,但其实是串行调度的,效率并非直接是N倍。

HeLei Blog

jvm01

发表于 2017-05-11 | 分类于 Java

阻塞io模型

阻塞io模型

HeLei Blog

java-multithread

发表于 2017-04-25 | 分类于 Java

##

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class test01 {
public static void main(String[] args) throws InterruptedException {
class Counter {
private int count = 0;
public void incre() {
count += 1;
}
public int getCount() {
return count;
}
}
final Counter counter = new Counter();
class CountingThread extends Thread {
public void run() {
for (int i = 0; i < 200000; ++i) {
System.out.println("thread id:"+this.getId());
counter.incre();
}
}
}
Thread thread1 = new CountingThread();
Thread thread2 = new CountingThread();
thread2.start();
thread1.start();
thread1.join();
thread2.join();
System.out.println("counter: "+counter.getCount());
}
}

1
counter: 399998

使用synchronized修饰方法

1
2
3
public synchronized void incre() {
count += 1;
}

使用synchronized修饰”关键区域”

1
2
3
4
5
public void incre() {
synchronized (this) {
count += 1;
}
}

synchronized不能继承——也就是说,假如一个方法在基础类中是“ 同步”的,那么在衍生类过载版本中,它不会自动进入“同步”状态。

1…5678
He Lei

He Lei

c/c++/python | redis | recommend algorithm | search engine

75 日志
16 分类
3 标签
GitHub Weibo
© 2018 He Lei
由 Hexo 强力驱动
主题 - NexT.Pisces