十大主流程序虚拟机深度解析:从架构到选型,一文看透PVM核心技术

程序虚拟机


十大主流程序虚拟机深度解析:从架构到选型,一文看透PVM核心技术

在现代软件开发中,程序虚拟机(PVM)是连接高级语言与底层硬件的核心桥梁,它不仅实现了“一次编译,到处运行”的跨平台梦想,更在不同场景下(企业级后端、前端、移动端、嵌入式等)承担着性能优化、资源管控、安全隔离的关键角色。

很多开发者对虚拟机的认知停留在“HotSpot=Java虚拟机”“V8=JS引擎”的表层,却忽略了它们背后截然不同的架构设计、编译策略和优化逻辑。今天,我们就来拆解十大主流虚拟机(HotSpot、V8、CLR、ART、Zend、PyPy、LuaJIT、BEAM、Wasmtime、GraalVM),从核心架构、JIT编译、内存管理、并发模型到生态选型,一文讲透虚拟机的技术本质与实战价值。

Continue reading 十大主流程序虚拟机深度解析:从架构到选型,一文看透PVM核心技术

V8引擎常用命令

1、测试文件test.js

function log(msg){
    console.log(msg)
}

hi = "hello"
log(hi)

2、输出AST语法树

d8 --print-ast test.js
[generating bytecode for function: ]
--- AST ---
FUNC at 0
. KIND 0
. LITERAL ID 0
. SUSPEND COUNT 0
. NAME ""
. INFERRED NAME ""
. DECLS
. . FUNCTION "log" = function log
. EXPRESSION STATEMENT at 47
. . ASSIGN at 50
. . . VAR PROXY unallocated (0000022CC5A21D98) (mode = DYNAMIC_GLOBAL, assigned = true) "hi"
. . . LITERAL "hello"
. EXPRESSION STATEMENT at 61
. . ASSIGN at -1
. . . VAR PROXY local[0] (0000022CC5A21CC8) (mode = TEMPORARY, assigned = true) ".result"
. . . CALL
. . . . VAR PROXY unallocated (0000022CC5A21B10) (mode = VAR, assigned = true) "log"
. . . . VAR PROXY unallocated (0000022CC5A21D98) (mode = DYNAMIC_GLOBAL, assigned = true) "hi"
. RETURN at -1
. . VAR PROXY local[0] (0000022CC5A21CC8) (mode = TEMPORARY, assigned = true) ".result"

[generating bytecode for function: log]
--- AST ---
FUNC at 12
. KIND 0
. LITERAL ID 1
. SUSPEND COUNT 0
. NAME "log"
. PARAMS
. . VAR (0000022CC5A21B68) (mode = VAR, assigned = false) "msg"
. DECLS
. . VARIABLE (0000022CC5A21B68) (mode = VAR, assigned = false) "msg"
. EXPRESSION STATEMENT at 24
. . CALL
. . . PROPERTY at 32
. . . . VAR PROXY unallocated (0000022CC5A21D18) (mode = DYNAMIC_GLOBAL, assigned = false) "console"
. . . . NAME log
. . . VAR PROXY parameter[0] (0000022CC5A21B68) (mode = VAR, assigned = false) "msg"

hello

3、输出作用域

d8 --print-scopes test.js
Inner function scope:
function log () { // (000001E067B50EA8) (12, 43)
  // 2 heap slots
  // local vars:
  VAR msg;  // (000001E067B54E18) never assigned
}
Global scope:
global { // (000001E067B50C88) (0, 68)
  // will be compiled
  // 1 stack slots
  // temporary vars:
  TEMPORARY .result;  // (000001E067B51288) local[0]
  // local vars:
  VAR log;  // (000001E067B510D0)
  // dynamic vars:
  DYNAMIC_GLOBAL hi;  // (000001E067B51358)

  function log () { // (000001E067B50EA8) (12, 43)
    // lazily parsed
    // 2 heap slots
  }
}
Global scope:
function log (msg) { // (000001E067B50EA8) (12, 43)
  // will be compiled
  // local vars:
  VAR msg;  // (000001E067B51128) parameter[0], never assigned
}
hello

4、输出字节码

d8 --print-bytecode test.js
[generated bytecode for function:  (0x03970824fc39 <SharedFunctionInfo>)]
Parameter count 1
Register count 3
Frame size 24
         000003970824FCEA @    0 : 12 00             LdaConstant [0]
         000003970824FCEC @    2 : 26 fa             Star r1
         000003970824FCEE @    4 : 27 fe f9          Mov <closure>, r2
         000003970824FCF1 @    7 : 61 37 01 fa 02    CallRuntime [DeclareGlobals], r1-r2
         000003970824FCF6 @   12 : 12 01             LdaConstant [1]
         000003970824FCF8 @   14 : 15 02 00          StaGlobal [2], [0]
         000003970824FCFB @   17 : 13 03 02          LdaGlobal [3], [2]
         000003970824FCFE @   20 : 26 fa             Star r1
         000003970824FD00 @   22 : 13 02 04          LdaGlobal [2], [4]
         000003970824FD03 @   25 : 26 f9             Star r2
         000003970824FD05 @   27 : 5d fa f9 06       CallUndefinedReceiver1 r1, r2, [6]
         000003970824FD09 @   31 : 26 fb             Star r0
         000003970824FD0B @   33 : aa                Return
Constant pool (size = 4)
000003970824FCB1: [FixedArray] in OldSpace
 - map: 0x0397080404b1 <Map>
 - length: 4
           0: 0x03970824fc61 <FixedArray[2]>
           1: 0x03970824fc01 <String[#5]: hello>
           2: 0x03970824fbf1 <String[#2]: hi>
           3: 0x0397081c692d <String[#3]: log>
Handler Table (size = 0)
Source Position Table (size = 0)
[generated bytecode for function: log (0x03970824fc71 <SharedFunctionInfo log>)]
Parameter count 2
Register count 2
Frame size 16
         000003970824FE4E @    0 : 13 00 00          LdaGlobal [0], [0]
         000003970824FE51 @    3 : 26 fa             Star r1
         000003970824FE53 @    5 : 28 fa 01 02       LdaNamedProperty r1, [1], [2]
         000003970824FE57 @    9 : 26 fb             Star r0
         000003970824FE59 @   11 : 59 fb fa 02 04    CallProperty1 r0, r1, a0, [4]
         000003970824FE5E @   16 : 0d                LdaUndefined
         000003970824FE5F @   17 : aa                Return
Constant pool (size = 2)
000003970824FE1D: [FixedArray] in OldSpace
 - map: 0x0397080404b1 <Map>
 - length: 2
           0: 0x0397081c68b9 <String[#7]: console>
           1: 0x0397081c692d <String[#3]: log>
Handler Table (size = 0)
Source Position Table (size = 0)
hello

5、测试文件test1.js

let a = {x:1}

function bar(obj) { 
  return obj.x 
}

function foo (count) { 
  let ret = 0
  for(let i = 1; i < count; i++) {
    ret += bar(a)
  }
  return ret
}

//foo(7049)
//foo(100000)

6、输出优化信息

//foo(7049)
d8 --trace-opt-verbose test1.js
[not yet optimizing foo, not enough ticks: 0/2 and ICs changed]

//foo(10000)
[not yet optimizing foo, not enough ticks: 0/2 and ICs changed]
[marking 0x01410824fe35 <JSFunction foo (sfi = 000001410824FCD9)> for optimized recompilation, reason: small function]
[compiling method 0x01410824fe35 <JSFunction foo (sfi = 000001410824FCD9)> using TurboFan OSR]
[optimizing 0x01410824fe35 <JSFunction foo (sfi = 000001410824FCD9)> - took 141.370, 62.753, 1.551 ms]

7、输出反优化信息

//没有输出。。。
d8 --trace-deopt test1.js

8、test2.js

function strToArray(str) {
  let i = 0
  const len = str.length
  let arr = new Uint16Array(str.length)
  for (; i < len; ++i) {
    arr[i] = str.charCodeAt(i)
  }
  return arr;
}

function foo() {
  let i = 0
  let str = 'test V8 GC'
  while (i++ < 1e5) {
    strToArray(str);
  }
}

foo()

9、输出反优化信息

d8 --trace-gc test2.js
[4600:0000019D00000000]      490 ms: Scavenge 1.2 (2.4) -> 0.3 (3.4) MB, 14.5 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      500 ms: Scavenge 1.2 (3.4) -> 0.3 (3.6) MB, 2.0 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      501 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      503 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      505 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      506 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      508 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      509 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      511 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      513 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      515 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      516 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      518 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure
[4600:0000019D00000000]      520 ms: Scavenge 1.3 (3.6) -> 0.3 (3.6) MB, 0.1 / 0.0 ms  (average mu = 1.000, current mu = 1.000) allocation failure

10、test3.js

function Foo(property_num,element_num) {
  //添加可索引属性
  for (let i = 0; i < element_num; i++) {
      this[i] = `element${i}`
  }
  //添加常规属性
  for (let i = 0; i < property_num; i++) {
      let ppt = `property${i}`
      this[ppt] = ppt
  }
}
var bar = new Foo(10,10)
console.log(%HasFastProperties(bar));
delete bar.property2
console.log(%HasFastProperties(bar));

11、使用内部方法

//%HasFastProperties测试是否有快属性
d8 --allow-natives-syntax test3.js
true
false

编译V8引擎

1、安装Visual Studio 2017

2、从微软下载Windows 10 SDK,安装“Debugging Tools for Windows”

https://developer.microsoft.com/en-US/windows/downloads/windows-10-sdk/

3、设置代理

# git的http代理设置
git config --global http.proxy 127.0.0.1:9528
git config --global https.proxy 127.0.0.1:9528

# 可以通过操作系统设置http代理
# 也可以通过通过命令行设置http代理设置(管理员权限)
netsh winhttp set proxy 127.0.0.1:9528

# 设置cipd_client的http代理设置
set HTTP_PROXY=127.0.0.1:9528
set HTTPS_PROXY=127.0.0.1:9528

4、环境变量

# 启动 VS2017 x86_amd64 命令行环境
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsx86_amd64.bat"

# 环境变量
set PATH=D:\GitGoogleV8\depot_tools_win;D:\NeoLang\Python\Python37;%PATH%
set DEPOT_TOOLS_WIN_TOOLCHAIN=0

5、下载源码

mkdir V8Build
cd V8Build
# V8Build
fetch v8

cd v8
# V8Build/v8
git pull origin 8.3.82
cd ..

6、同步环境

# V8Build
gclient sync

7、生成解决方案

cd v8/src
# V8Build/v8/src
gn gen --ide=vs out\Default
# 也可以设置filters,如果是看chrome源码,最好设置一下,因为有几千个项目
# gn gen --ide=vs --filters=//chrome --no-deps out\Default

8、编译

# 用vs打开sln,并编译
# 编译过程中,有时会出现文件无法访问等情况
# 可以在命令行中执行,然后用VS继续编译就可以成功了
# V8Build/v8/src
devenv out\Default\all.sln

9、测试

# V8Build/v8/src
cd out\Default
# V8Build/v8/src/out/Default
v8_shell.exe

JSP输出容器的JVM参数

<%@ page language="java" import="java.util.*, java.lang.management.*" pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>系统参数输出</title>
<style type="text/css">
table
{
    border-collapse:collapse;
}
td
{
    border:1px solid #427BD6;
}
</style>
</head>

<body style="background-color:#f2f2f2;">

<h1>环境变量</h1>
<table>
<%
    Map<String, String> map = System.getenv();
    //RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
    //Map<String, String> map = runtimeMxBean.getSystemProperties();
    for(String key : map.keySet())
    {
        out.println("<tr>");
        out.println("<td>");
        out.println(key);
        out.println("</td>");
        out.println("<td>");
        out.println(map.get(key));
        out.println("</td>");
        out.println("</tr>");
    }     
%>
</table>

<h1>JVM参数</h1>
<table>
<% 
    RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
    
    out.println("<tr>");
    out.println("<td>");
    out.println(runtimeMxBean.getVmVendor()+" || "+runtimeMxBean.getVmName() +" || "+ runtimeMxBean.getVmVersion());
    out.println("</td>");
    out.println("</tr>");
    
    out.println("<tr>");
    out.println("<td>");
    out.println(runtimeMxBean.getSpecVendor()+" || "+runtimeMxBean.getSpecName() +" || "+ runtimeMxBean.getSpecVersion());
    out.println("</td>");
    out.println("</tr>");
    
    out.println("<tr>");
    out.println("<td>");
    out.println(runtimeMxBean.getName()+" || "+runtimeMxBean.getStartTime() +" || "+ runtimeMxBean.getUptime());
    out.println("</td>");
    out.println("</tr>");
    
    out.println("<tr>");
    out.println("<td>");
    out.println("MaxMemory="+Runtime.getRuntime().maxMemory()/1024/1024 + "m ");
    out.println("TotalMemory="+Runtime.getRuntime().totalMemory()/1024/1024 + "m ");
    out.println("FreeMemory="+Runtime.getRuntime().freeMemory()/1024/1024 + "m ");
    out.println("</td>");
    out.println("</tr>");
    
    List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
    for (MemoryPoolMXBean bean : memoryPoolMXBeans)
    {
        out.println("<tr>");
        out.println("<td>");
        out.println(bean.getName()+" || "+bean.getType()+" || Init="+bean.getUsage().getInit()/1024/1024+
        "m Max="+bean.getUsage().getMax()/1024/1024+"m Used="+bean.getUsage().getUsed()/1024/1024+
        "m Committed="+bean.getUsage().getCommitted()/1024/1024+"m ");
        out.println("</td>");
        out.println("</tr>");
    }
    
    List<String> arguments = runtimeMxBean.getInputArguments();
    for(String arg:arguments)
    {
        out.println("<tr>");
        out.println("<td>");
        out.println(arg);
        out.println("</td>");
        out.println("</tr>");
    }
%>
</table>

<h1>系统参数</h1>
<table>
<%
    Properties props = System.getProperties();
    for(Object o:props.keySet())
    {
        out.println("<tr>");
        out.println("<td>");
        out.println(o);
        out.println("</td>");
        out.println("<td>");
        out.println(props.get(o));
        out.println("</td>");
        out.println("</tr>");
    }
%>
</table>
</body>
</html>

Java四种引用方式

最近写了个例子,说明了一下强引用、软引用、弱引用、虚引用的区别。

1、NString.java

package com.neohope.reference;

/**
 * Created by Hansen
 */
public class NString {
    private String name;
    private String value;

    public NString(String name, String value)
    {
        this.name = name;
        this.value=value;
    }

    public NString(String name, StringBuilder builder)
    {
        this.name = name;
        this.value=builder.toString();
    }

    public String Name()
    {
        return name;
    }

    public String Value()
    {
        return value;
    }

    @Override
    protected void finalize()
    {
        System.out.println(">> "+Name()+" finalize called");
        try {
            super.finalize();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

2、TestReferenceA.java

package com.neohope.reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;

/**
 * Created by Hansen
 * JVM参数:
 * -XX:+PrintGCDetails
 * -Xms6m -Xmx12m
 */
public class TestReferenceA {
    public static void main(String[] args) {
        //申请内存
        Runtime rt=Runtime.getRuntime();
        System.out.println("Total memory is "+rt.totalMemory());
        int MemSize = 1024*1024*1;
        StringBuilder builder = new StringBuilder(MemSize);
        for (int i=0;i<MemSize/8;i++) {
            builder.append("ABCDEFGH");
        }
        System.out.println("Total memory is "+rt.totalMemory());

        //用s00与s01两个大字符串演示强引用与软引用的区别
        NString s00 = new NString("s00",builder);
        NString s01 = new NString("s01",builder);
        //s02与s03演示WeakReference的两种用法
        NString s02 = new NString("s02","s03");
        String key03 = new String("k03");
        NString s03 = new NString("s03","s03");
        //s04演示PhantomReference
        NString s04 = new NString("s04","s04");
        System.out.println("Total memory is "+rt.totalMemory());

        //第1类、强引用的对象,也最常用到的类型,内存不足时JVM将抛出异常也不要释放这些内存
        NString strongRef = s00;
        //第2类、只有软引用的数据,内存不足时JVM会回收这些内存
        SoftReference<NString> softRef = new SoftReference<NString>(s01);
        //第3类、只有弱引用的数据,无论内存是否充足JVM会回收这些内存,也就是弱引用不会影响对象的生命周期
        WeakReference<NString> weakRef = new WeakReference<NString>(s02);
        //WeakHashMap很有用,当key022为null时,s022也就会被GC咯
        WeakHashMap weakHashMap = new WeakHashMap();
        weakHashMap.put(key03,s03);
        //第4类、只有虚引用的数据,一开始refQueue为空,被GC才会有值
        ReferenceQueue refQueue = new ReferenceQueue<String>();
        PhantomReference<NString> phantomRef = new PhantomReference<NString>(s04, refQueue);

        //有强引用时,输出初始化情况
        System.out.println("With strong ref strongRef is " + (strongRef==null?"null":"not null"));
        System.out.println("With strong ref softRef is " + (softRef.get()==null?"null":"not null"));
        System.out.println("With strong ref weakRef is " + (weakRef.get()==null?"null":"not null"));
        System.out.println("With strong ref weakHashMap is " + (weakHashMap.isEmpty() ?"empty":"not empty"));
        //这里为null,当对象被GC时,会得到虚引用对象
        System.out.println("With strong ref refQueue has " + refQueue.poll());
        System.out.println();

        //去掉初始的强引用
        s00 = null;
        s01 = null;
        s02 = null;
        s03 = null;
        s04 = null;

        //多数情况下,没有来得及GC
        System.out.println("Before gc strongRef is " + (strongRef==null?"null":"not null"));
        System.out.println("Before gc softRef is " + (softRef.get()==null?"null":"not null"));
        System.out.println("Before gc weakRef is " + (weakRef.get()==null?"null":"not null"));
        System.out.println("Before gc weakHashMap is " + (weakHashMap.isEmpty() ?"empty":"not empty"));
        //null,当对象被GC时,会得到虚引用对象
        System.out.println("Before gc refQueue has " + refQueue.poll());
        System.out.println();

        //强制GC
        System.gc();
        System.out.println("After first gc strongRef is " + (strongRef==null?"null":"not null"));
        System.out.println("After first gc softRef is " + (softRef.get()==null?"null":"not null"));
        //null,weakRef已经被释放了
        System.out.println("After first gc weakRef is " + (weakRef.get()==null?"null":"not null"));
        System.out.println("After first gc weakHashMap is " + (weakHashMap.isEmpty() ?"empty":"not empty"));
        //与运行环境及GC方法有关,会出现一次有值的情况,然后一直为null
        System.out.println("After first gc refQueue has " + refQueue.poll());
        System.out.println();

        //将key03设为null,强制GC
        key03 = null;
        System.gc();
        System.out.println("After second gc strongRef is " + (strongRef==null?"null":"not null"));
        System.out.println("After second gc softRef is " + (softRef.get()==null?"null":"not null"));
        //null
        System.out.println("After second gc weakRef is " + (weakRef.get()==null?"null":"not null"));
        //empty,由于key被设为空,则会释放掉弱引用对象了
        System.out.println("After second gc weakHashMap is " + (weakHashMap.isEmpty() ?"empty":"not empty"));
        //与运行环境及GC方法有关,会出现一次有值的情况,然后一直为null
        System.out.println("After second gc refQueue has " + refQueue.poll());
        System.out.println();

        //Finalization
        System.runFinalization();
        System.out.println("After Finalization strongRef is " + (strongRef==null?"null":"not null"));
        System.out.println("After Finalization softRef is " + (softRef.get()==null?"null":"not null"));
        //null
        System.out.println("After Finalization weakRef is " + (weakRef.get()==null?"null":"not null"));
        //empty
        System.out.println("After Finalization weakHashMap is " + (weakHashMap.isEmpty() ?"empty":"not empty"));
        //与运行环境及GC方法有关,会出现一次有值的情况,然后一直为null
        System.out.println("After Finalization refQueue has " + refQueue.poll());
        System.out.println();

        //申请内存,造成内存不足
        System.out.println("Total memory is "+rt.totalMemory());
        StringBuilder builder2 = new StringBuilder(MemSize);
        for (int i=0;i<MemSize/8;i++) {
            builder2.append("ABCDEFGH");
        }
        NString tmp0 = new NString("tmp0", builder2);
        System.out.println("Total memory is "+rt.totalMemory());

        System.out.println("When mem is low strongRef is " + (strongRef==null?"null":"not null"));
        //null,内存不足时软引用会被释放
        System.out.println("When mem is low softRef is " + (softRef.get()==null?"null":"not null"));
        //null
        System.out.println("When mem is low weakRef is " + (weakRef.get()==null?"null":"not null"));
        //empty
        System.out.println("When mem is low weakHashMap is " + (weakHashMap.isEmpty() ?"empty":"not empty"));
        //与运行环境及GC方法有关,会出现一次有值的情况,然后一直为null
        System.out.println("When mem is low refQueue has " + refQueue.poll());
        System.out.println();

        //再次申请内存,JMV会内存爆掉
        NString tmp1 = null;
        try
        {
            tmp1 = new NString("tmp1",builder2);
        }
        catch(OutOfMemoryError ex)
        {
            System.out.println("Not enough memory");
        }

        //null
        System.out.println("tmp1 is " + (tmp1==null?"null":"not null"));
        //就算是抛出OutOfMemoryError,仍然不会释放
        System.out.println("When not enough memory strongRef is " + (strongRef==null?"null":"not null"));
        //null
        System.out.println("When not enough memory softRef is " + (softRef.get()==null?"null":"not null"));
        //null
        System.out.println("When not enough memory weakRef is " + (weakRef.get()==null?"null":"not null"));
        //empty
        System.out.println("When not enough memory weakHashMap is " + (weakHashMap.isEmpty() ?"empty":"not empty"));
        //null
        System.out.println("When not enough memory refQueue has " + refQueue.poll());
        System.out.println();
    }
}

3、输出

Total memory is 6094848
Total memory is 6094848
Total memory is 11390976
With strong ref strongRef is not null
With strong ref softRef is not null
With strong ref weakRef is not null
With strong ref weakHashMap is not empty
With strong ref refQueue has null

Before gc strongRef is not null
Before gc softRef is not null
Before gc weakRef is not null
Before gc weakHashMap is not empty
Before gc refQueue has null

After first gc strongRef is not null
After first gc softRef is not null
After first gc weakRef is null
After first gc weakHashMap is not empty
>> s04 finalize called
After first gc refQueue has null
>> s02 finalize called

After second gc strongRef is not null
After second gc softRef is not null
After second gc weakRef is null
After second gc weakHashMap is not empty
After second gc refQueue has java.lang.ref.PhantomReference@15d63da

After Finalization strongRef is not null
After Finalization softRef is not null
After Finalization weakRef is null
After Finalization weakHashMap is not empty
After Finalization refQueue has null

Total memory is 14221312
Total memory is 14221312
When mem is low strongRef is not null
When mem is low softRef is not null
When mem is low weakRef is null
When mem is low weakHashMap is not empty
When mem is low refQueue has null

>> s01 finalize called
Not enough memory
tmp1 is null
When not enough memory strongRef is not null
When not enough memory softRef is null
When not enough memory weakRef is null
When not enough memory weakHashMap is empty
When not enough memory refQueue has null

Java程序如何加密

今天在想Java程序加密的事情,现在市面上的方法,无非是混淆代码,jar包签名等,瞬间就能被破解掉。

我想到了一个很挫的方法,和PE文件加壳脱壳一样,class文件/jar文件为什么不可以呢?

但这样做的话,是有限制的,就是客户必须使用你自己定制的JVM及容器,否则是无法运行的。

具体方法如下:
1、生成class文件后,按一定规则进行加密处理。偷懒的话,直接对称加密好了。
2、生成jar包的时候,同样按一定规则进行加密处理。偷懒的话,zip的时候,增加一个强壮的密码就好了。
3、下载并编译OpenJDK,在读取jar包内容,和class文件的地方,要进行脱壳处理。按上面的思路,就是解压缩和解密处理。
4、当然,定制部分的dll和exe,需要进行PE加壳处理

当然,上面这种方式的话,和加壳脱壳还是有很大的区别的,就是不能自行解压,并要依赖于JVM甚至容器的定制。

如果要真正实现自解压处理的话,就要多做几步:

具体方法如下:
1、编译生成class文件,按一定规则进行加密处理。偷懒的话,直接对称加密好了。
2、生成jar包的时候,同样按一定规则进行加密处理。偷懒的话,zip的时候,增加一个强壮的密码就好了。
3、将需要的所有jar包,按你喜欢的方法,生成一个jar包列表,并打成一个巨大的资源文件。
4、写一个java引导文件,用于处理运行参数,比如入口程序等。
5、写一个自定义classloader+jni+dll,用于读取巨大的资源文件中的jar包及class文件
6、用普通的jvm启动程序,初始化时用引导文件+自定义classloader
7、引导文件+自定义classloader将需要的文件直接解压到内存中,提供给jvm使用
8、dll部分要进行PE加壳处理

JVM常用参数设置

运行方式设置
-client
-server
两者的区别在JIT的编译比例及GC的方式不同,也可以近似的这样理解:client会启动比较快,GC默认为串行,但JIT编译较少,速度慢一些;server启动慢,GC默认为并行,但JIT编译较多,开始速度慢,但JIT编译后会越来越快。

内存设置
-Xms
设置初始化堆的大小

-Xmx
设置最大堆大小

-Xss 或 -XX:ThreadStackSize
设置每个线程栈大小

-Xmn 或 -XX:NewSize
设置年轻代的内存大小

-XX:MaxNewSize
设置最大年轻代新生代内存大小

-XX:PermSize
设置持久代内存大小

-XX:MaxPermSize
设置最大值持久代内存大小

-XX:NewRatio
设置年轻代与年老代的比值

-XX:SurvivorRatio
设置年轻代中Eden区与Survivor区的大小比值

GC设置
-XX:+DisableExplicitGC
禁用java代码中的System.gc()

-XX:+UseSerialGC
设置串行收集器

-XX:+UseParallelGC 或 -XX:+UseParNewGC
设置年轻代为并行收集器

-XX:ParallelGCThreads
设置并行收集器的线程数

或 -XX:+UseParallelOldGC
设置年老代为并行收集

-XX:+UseConcMarkSweepGC
设置年老代为CMS收集

-XX:MaxGCPauseMillis
一次GC最大的停顿时间

-XX:GCTimeRatio
GC占用CPU时间比例

调试GC
-verbose:gc
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCApplicationStoppedTime
-XX:PrintHeapAtGC

调试加载类
-verbose:class 监视加载的类的情况

调试JNI
-verbose:jni

设置字符集为UTF-8
-Djavax.servlet.request.encoding=utf-8
-Dfile.encoding=utf-8

调试SSL
-Djavax.net.debug=ssl:record
-Djavax.net.debug=ssl:handshake
-Djavax.net.debug=all

内存溢出时抓取JVM快照
-XX:+HeapDumpOnOutOfMemoryError