Sep 22

cms垃圾回收的生命周期

转自:https://blogs.oracle.com/jonthecollector/entry/hey_joe_phases_of_cms

CMS (-XX:+UseConcMarkSweepGC) or the concurrent mark sweep GC could have been called the mostly concurrent mark sweep GC and here’s why.

These are the phases of a CMS concurrent collection.

1. Initial mark. This is the the start of CMS collection. This is a stop-the-world phase. All the applications threads are stopped and CMS scans the application threads (e.g. the thread stacks) for object references.

2. Concurrent mark. This is a concurrent phase. CMS restarts all the applications threads and then starting from the objects found in 1), it finds all the other live objects. Almost (see the remark phase).

3. Precleaning phase. This is a concurrent phase. This phase is an optimization and you don’t really need to understand what it does so feel free to skip to 4. While CMS is doing concurrent marking (2.), the application threads are running and they can be changing the objects they are using. CMS needs to find any of these changes and ultimately does that during the remark phase. However, CMS would like to discovery this concurrently and so has the precleaning phase. CMS has a way of recording when an application thread makes a change to the objects that it is using. Precleaning looks at the records of these changes and marks live objects as live. For example if thread AA has a reference to an object XX and passes that reference to thread BB, then CMS needs to understand that BB is keeping XX alive now even if AA no longer is.

4. The remark phase. The remark phase is a stop-the-world. CMS cannot correctly determine which objects are alive (mark them live), if the application is running concurrently and keeps changing what is live. So CMS stops all the application threads during the remark phase so that it can catch up with the changes the application has been making.

5. The sweep phase. This is a concurrent phase. CMS looks at all the objects and adds newly dead objects to its freelists. CMS does freelist allocation and it is during the sweep phase that those freelists get repopulated.

6. The reset phase. This is a concurrent phase. CMS is cleaning up its state so that it is ready for the next collection.

Feb 23

JVM内存模型以及垃圾收集策略解析

 

 一 JVM内存模型

1.1 Java栈

Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文。栈空间随着线程的终止而释放。

StackOverflowError:如果在线程执行的过程中,栈空间不够用,那么JVM就会抛出此异常,这种情况一般是死递归造成的。 Continue reading

Dec 29

Java JNI学习

    07年的时候就用过JNI这东西了,那时候是为了给项目的J2EE应用写一个授权功能,通过调用vc写的读取硬件id的dll使得cdkey能够跟机器一一对应起来,从而做到控制一个cdkey只能装到一台机器上上。那会儿用的dll是别人写的,java中调用dll也是用的小日本儿写的一个叫jcom的组件,这次决定自己做一遍整个流程.

  1. 编写java文件
    /**
     * Copyright (C), 2009-2010
     * File Name: jni.WinMessage.java
     * Encoding UTF-8
     * Version: 1.0
     * Date: Dec 29, 2009
     * History:	
     */
    package jni;
    
    /**
     *
     * @author (bestirwiny@gmail.com)
     * @version Revision: 1.00 2:12:24 PM
     */
    public class WinMessage
    {
    	static{
    		System.loadLibrary("WinMsgDll");
    	}
    	public native void showMsgBox(String msg);
    }
    

    loadLibrary用于load一个dll,该dll应该在java.library.path定义的路径下啊。

  2. 用javac编译该文件,不用讲。
  3. javah -jni WinMessage,会生成一个jni_WinMessage.h的头文件,下面要做的工作就是dll的编写了。
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include "jni.h"
    /* Header for class jni_WinMessage */
    
    #ifndef _Included_jni_WinMessage
    #define _Included_jni_WinMessage
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     jni_WinMessage
     * Method:    showMsgBox
     * Signature: (Ljava/lang/String;)V
     */
    JNIEXPORT void JNICALL Java_jni_WinMessage_showMsgBox
      (JNIEnv *, jobject, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
  4. 创建一个win32动态连接库的工程,将$JAVAHOME/include,$JAVA_HOME/include/win32目录加入到工程需要引用的头文件路径中。
    #include "tchar.h"
    #include "stdlib.h"
    #include "stdio.h"
    #include "AtlBase.h"
    #include "AtlConv.h"
    #include "windows.h"
    #include "jni_WinMessage.h"
    
    /*
     * Class:    
     * Method:    showMsgBox
     * Signature: (Ljava/lang/String;)V
     */
    JNIEXPORT void JNICALL Java_jni_WinMessage_showMsgBox
      (JNIEnv * env, jobject jobj, jstring str){
    	const char * msg;
    	msg = env->GetStringUTFChars(str,0);
    
    	WCHAR* strA;
        int i= MultiByteToWideChar ( CP_UTF8 , 0 ,msg ,-1 ,NULL,0);
    	strA = new WCHAR[i];
    	MultiByteToWideChar ( CP_UTF8 , 0 ,( char * ) msg, -1, strA , i );
    
    	USES_CONVERSION;
    	MessageBox(NULL,strA,_T("Java invoke "),MB_OK);
    	env->ReleaseStringUTFChars(str,msg);
    }
    

    由于win32变成才开始学,所以这个东西弄了好久才弄好,网上的例子中通过GetStringUTFChars取回的msg直接传到MessageBox中用,可我用的是VS2008,直接用会报错误,大致是说不能将const char *转换为LPCWSTR * ,
    LPCWSTR是long pointer constant wide string的意思,是一种wchar_t类型,char类型占用一个byte,而wchar_t占用两个byte,所以这边就要用一个转换 函数,网上查好久才查到MutiByteToWideChar函数,才解决了中文乱码的问题。

  5. 编译完会成成一个dll文件,拷贝到java.library.path下,我是拷贝到了c:\windows下。
  6. 编写一个java类调用上面的WinMessage
    /**
     * Copyright (C), 2009-2010
     * File Name: jni.TestWinMessage.java
     * Encoding UTF-8
     * Version: 1.0
     * Date: Dec 29, 2009
     * History:	
     */
    package jni;
    
    /**
     *
     * @author (bestirwiny@gmail.com)
     * @version Revision: 1.00 4:54:25 PM
     */
    public class TestWinMessage
    {
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args)
    	{
    		WinMessage wm = new WinMessage();
    		wm.showMsgBox("MC Hotdog,哈狗帮!");
    	}
    }
    
  7. ok,这下就完成了从java程序中调用win32 API弹出windows原生的窗口了 。
Jan 09

Java位运算

      Java算是一门很高级的语言了,平时使用一般不会用到位运算这种很低级的特性,但在有些地方有很高的性能或者空间的要求可能会用到,比如说手机平台。今天偶尔看了下,发现一些基础只是很长时间没有搞都忘记了,今天看到相关的东西以至于想了很久才想通。就从今天看到的一段代码来回顾些基础知识,是一段将int转换为byte数组的代码。

———————————————————–
//cout为自己包装的打印方法
  int a = 645765765;
  cout(a);
  cout(Integer.toBinaryString(a));
  byte[] b = new byte[4];
     b[0] = (byte)((a>>24)&0xff);
     cout(Integer.toBinaryString(b[0]&0xff));
     b[1] = (byte)((a>>16)&0xff);
     cout(Integer.toBinaryString(b[1]&0xff));
     b[2] = (byte)((a>>8)&0xff);
     cout(Integer.toBinaryString(b[2]&0xff));
     b[3] = (byte)((a>>0)&0xff);
     cout(Integer.toBinaryString(b[3]&0xff));
    
     int decodeA ;
     decodeA = b[0]&0x000000ff;
     decodeA = (decodeA<<8) + (b[1]&0xff);
     decodeA = (decodeA<<8) + (b[2]&0xff);
     decodeA = (decodeA<<8) + (b[3]&0xff);
     cout(decodeA);
//自己写的另外一种转换方法
     int decodeA2 ;
     decodeA2  = ((b[0]&0xff)<<24);
     decodeA2 += ((b[1]&0xff)<<16);
     decodeA2 += ((b[2]&0xff)<<8);
     decodeA2 += ((b[3]&0xff)<<0);
     cout(decodeA2);
——————————————————————–

      一开始看到的黑体部分。于是写了下面红色的版本,但之前不是现在这个正确版本,之前是这样的。
     int decodeA2 ;
     decodeA2  = b[0]<<24;
     decodeA2 += b[1]<<16);
     decodeA2 += b[2]<<8;
     decodeA2 += b[3]<<0;
     cout(decodeA2);
    结果只要a的值一超过128就会出错,百思不得其解,之后查了有关移位的资料。右移在java中有两种方式,一种是带符号的,一种是不带符号的,带符号的会根据最高位的值来判断用1还是0来补足,若最高位为1则用1来补,若为0则用0来补,无符号的则全部用0补,而对于左移则全部用0补。想了很久这个似乎也没有关系,最后偶然间看到byte的最高位是用来表示正负的,就是说byte的值为-128到127,所以只要在遇到了某一byte的最高位为1的情况下就会出现该值变为了一负值,但其实这个时候它里面存的位信息还是对的,用Integer的int到二进制的一个方法将其打出来便可以发现是一个32位的高位全为1的二进制形式,及是一个负数,于是对它在进行一次“与运算”去掉高位的“1”就可以得到正确的结果了。