`
lzy.je
  • 浏览: 150559 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

JRE Hack 浅度研究

阅读更多

          很多时候对应用软件代码层面的性能调优受到很多主观、客观条件的影响,本文所述的 JRE Hack 就是在这样的背景下展开的。当前的应用中需要记录大量的调试信息,程序直接采用了 System.out.println 方法来将这些内容输出到日志文件中(WebSphere 的 outputStreamRedirect 默认配置到了 ${SERVER_LOG_ROOT}/SystemOut.log 文件),因此 System.out.println 方法大量遍布在整个程序代码中。而由于该方法是线程安全的,即被同步(临界区保护)形成了串行执行,结果造成整体业务处理串行点很多,性能比较低下。解决 方法也很简单,就是避免使用 System.out.println 方法来进行日志输出减少串行瓶颈,可换用支持异步输出的 log4j 组件来实现日志功能。本来这个事情通过代码批量替换就可以完成,但实际诸多原因造成不允许修改应用代码,那么该怎么解决这个问题呢?

          这样的前提就不得以造就这样一个方法,通过把 JRE 中提供的 System.out.println 方法实现代码替换成使用 log4j 来进行 Log 输出的实现代码。这里并不打算评论该方法的好坏(实际上 JRE 中使用了 PrintStream 类的地方不少,该方法影响面较大),本文姑且作为对 JRE 内部粗浅研究的记录吧。

          应用使用的是 IBM JRE 5.0,JRE 本身是开源的。用 DJ Java Decompiler 工具也可以反编译得到相关 Java 类源码(System.class、System$1.class 位于 jre/lib/vm.jar 中)。找到 java/lang/System.java 文件,可以看到 stdin、stdout、stderr 都是 PrintStream 类实例:

 

// Typically, these are connected to the shell which
// ran the Java program.
/**
 * Default input stream
 */
public static final InputStream in;
/**
 * Default output stream
 */
public static final PrintStream out;
/**
 * Default error output stream
 */
public static final PrintStream err;

 

          在 java/io/PrintStream.java(PrintStream.class 位于 jre/lib/core.jar 中)文件中可以看到 println(String) 等相关方法定义如下:

 

/**
 * Print a String and then terminate the line.  This method behaves as
 * though it invokes <code>{@link #print(String)}</code> and then
 * <code>{@link #println()}</code>.
 *
 * @param x  The <code>String</code> to be printed.
 */
public void println(String x) {
	synchronized (this) {
		print(x);
		newLine();
	}
}

/**
 * Print a string.  If the argument is <code>null</code> then the string
 * <code>"null"</code> is printed.  Otherwise, the string's characters are
 * converted into bytes according to the platform's default character
 * encoding, and these bytes are written in exactly the manner of the
 * <code>{@link #write(int)}</code> method.
 *
 * @param  s   The <code>String</code> to be printed
 */
public void print(String s) {
	if (s == null) {
		s = "null";
	}
	write(s);
}

private void write(String s) {
	try {
	synchronized (this) {
		ensureOpen();
		textOut.write(s);
		textOut.flushBuffer();
		charOut.flushBuffer();
		if (autoFlush && (s.indexOf('\n') >= 0))
		out.flush();
	}
	}
	catch (InterruptedIOException x) {
	Thread.currentThread().interrupt();
	}
	catch (IOException x) {
	trouble = true;
	}
}

 

          此时,如果 hack 代码很简单的话,那么就可以将其直接写入到 PrintStream.java 相关方法中。但这样一来,加入的 hack 代码会与 JRE 中这些 Java 标准类库代码混在一起。更多时候我们为了便于维护,有必要将这些 hack 代码放在另外的独立 jar 文件中,这会引出一些问题,下面慢慢道来。

          让我们先再看看 System 类的 completeInitialization 方法代码,注意 Sun JRE 中 System 类实现并没有该方法:

 

static void completeInitialization() {
	setIn(com.ibm.JVM.io.ConsoleInputStream.localize(new BufferedInputStream(new FileInputStream(FileDescriptor.in))));
	Terminator.setup();
	// initialize the getName cache
	Class.setClassNameMap();
	com.ibm.misc.SystemIntialization.lastChanceHook();
}
 

          对于上面提出的需求,可以在这里直接加入我们 hack 的 PrintStream 实现:

 

import com.lzy.javaeye.fooattach.FooPrintStream;

... ...

static void completeInitialization() {
	setIn(com.ibm.JVM.io.ConsoleInputStream.localize(new BufferedInputStream(new FileInputStream(FileDescriptor.in))));

	// Hack code
	setOut(new FooPrintStream(out, true));
	setErr(new FooPrintStream(err, true));
	// Hack code end

	Terminator.setup();
	// initialize the getName cache
	Class.setClassNameMap();
	com.ibm.misc.SystemIntialization.lastChanceHook();
}

... ...

 

          这里使用的 com.lzy.javaeye.fooattach.FooPrintStream 类被单独封装在了 FooPrintStream.jar 文件中,实现代码如下:

 

package com.lzy.javaeye.fooattach;

import java.io.PrintStream;
import java.io.OutputStream;

public final class FooPrintStream extends PrintStream {

    public FooPrintStream(OutputStream outputstream, boolean flag)
    {
        super(outputstream, flag);
        
        ps = new PrintStream(outputstream, flag);
    }

    public void println(String s)
    {
    	ps.println(s + " - [length: " + s.length() + "]");
    }
    
    private PrintStream ps;
}

 

          这里仅用于说明,为了简便其中的 println 方法并没有直正调用 log4j 做 Log 输出,而是简单的在输出 String 加上了长度信息。

 

          接下来把 FooAttach.jar 文件放入 JRE HOME 的 lib 目录中,该目录中有 vm.jar 和 core.jar 等一系列“核心”的 JRE class jar 文件。下面我们就可以将这个 hack 版的 System.java 文件编译了:

 

javac –cp %CLASSPATH%\FooAttach.jar System.java

 

          此时会生成 System.class 和 System$1.class(System.AccessController.doPrivileged 方法使用的匿名类)两个文件,将把这个编译好的“新” System 类放入到 jar/lib/vm.jar/java/lang 目录中。这里随便写个测试程序:

 

public class PrintTest {
	public static void main(String[] args) {
		System.out.println("test11");
	}
}

 

          心急的兄弟如果现在运行它,那会得到 NoClassDefFoundError 异常:

 

Exception in thread "main" java/lang/NoClassDefFoundError: com.lzy.javaeye.fooattach.FooPrintStream
at java/lang/System.completeInitialization (System.java:117)
at java/lang/Thread.<init> (Thread.java:124)
JVMJ9VM015W Initialization error for library jclscar_23(14): JVMJ9VM009E J9VMDllMain failed

 

          创建 Java 虚拟机失败的原因在于 vm.jar 是被 bootstrap class loader 加载的,其中的 System 类当被 bootstrap class loader 加载时由于找不到依赖的 FooAttach.jar 包,造成抛出 FooPrintStream 类未定义异常,最终导致整个 JVM 初使化失败。

          下面是些关于 Java 类加载方面的科普知识,理解的兄弟请自行快速跳过。

 

          关于 Java 类是如何被加载的,可参见Sun提供的一篇文章:
          How Classes are Found

          还有一篇 IBM 写的关于类加载器相关问题诊断的文篇,也很好的说明了类加载问题:
          Demystifying class loading problems, Part 1: An introduction to class loading and debugging tools

          这里列出 Java 命令行 classpath 相关选项的说明:

 

command-line_classpath_options

 

          我们也可以通过 JVM heap dump 分析得到类加载的相关信息。在下面的示例 heap dump 中可以看到 3 个类加载及其加载的类信息:

 

本质上有以下 4 种生成 Java heap dump 的事件:

* A fatal native exception occurs;
* The JVM runs out of heap space;
* A signal is sent to the JVM (for example, if Control-Break is pressed on Windows, or Control-\ on Linux);
* The com.ibm.JVM.Dump.JavaDump() method is called.

 

 

heapdump_classloader_sample

 

          其中显示的 3 个类加载器分别是:

 

  1. The system class loader (sun/misc/Launcher$AppClass loader);
  2. The extension class loader (sun/misc/Launcher$ExtClass loader);
  3. The bootstrap class loader (*System*).


          上面已经说明,发生 NoClassDefFoundError 异常并导致 JVM 初使化失败的原因是由于我们生成的 FooAttach.jar 包没有被 bootstrap class loader 加载造成的。那么我们只需要通过 Java 的 -Xbootclasspath 启动选项将 FooAttach.jar 包含进 bootstrap class loader 加载列表即可:(C:\Program Files\Java_ibm1.5\jre 是我本机环境的 JRE HOME)

 

-Xbootclasspath:”C:\Program Files\Java_ibm1.5\jre\lib\FooAttach.jar;C:\Program Files\Java_ibm1.5\jre\lib\vm.jar;C:\Program Files\Java_ibm1.5\jre\lib\core.jar;C:\Program Files\Java_ibm1.5\jre\lib\charsets.jar;C:\Program Files\Java_ibm1.5\jre\lib\graphics.jar;C:\Program Files\Java_ibm1.5\jre\lib\security.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmpkcs.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorb.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcfw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorbapi.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjcefw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjgssprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjsseprovider2.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaaslm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaasactivelm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcertpathprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\server.jar;C:\Program Files\Java_ibm1.5\jre\lib\xml.jar”

 

          此时添加了上述 -Xbootclasspath 选项后再运行测试程序就可以得到正确结果了。尝试加入 System.getProperties().list(System.out); 代码,我们可以看到当前 JVM 系统属性信息:

 

java.assistive => ON
java.runtime.name => Java(TM) 2 Runtime Environment, Standard Edition
ibm.signalhandling.rs => false
sun.boot.library.path => C:\Program Files\Java_ibm1.5\jre\bin
java.vm.version => 2.3
com.ibm.oti.configuration => scar
java.vm.vendor => IBM Corporation
java.vendor.url => http://www.ibm.com/
path.separator => ;
java.vm.name => IBM J9 VM
user.country => CN
java.vm.specification.name => Java Virtual Machine Specification
user.dir => C:\Documents and Settings\Administrator\My Documents\workspace\PrintTest
java.runtime.version => 2.3
java.fullversion => J2RE 1.5.0 IBM J9 2.3 Windows XP x86-32 j9vmwi3223-20060504 (JIT enabled)
J9VM - 20060501_06428_lHdSMR
JIT - 20060428_1800_r8
GC - 20060501_AA
java.awt.graphicsenv => sun.awt.Win32GraphicsEnvironment
os.arch => x86
com.ibm.vm.bitmode => 32
java.io.tmpdir => C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\
line.separator =>

com.ibm.util.extralibs.properties =>
java.vm.specification.vendor => Sun Microsystems Inc.
user.variant =>
java.awt.fonts =>
os.name => Windows XP
sun.java2d.fontpath =>
sun.jnu.encoding => GB18030
java.library.path => C:\Program Files\Java_ibm1.5\jre\bin;.;C:\Program Files\Java_ibm1.5\jre\bin;C:/Program Files/Java/jre6/bin/client;C:/Program Files/Java/jre6/bin;C:\Program Files\Common Files\NetSarang;C:\Program Files\ruby-1.8.7-p72\bin;C:\oracle\product\10.2.0\client\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\TortoiseSVN\bin;C:\Program Files\JProbe 8.1\bin;C:\Program Files\IBM\Rational AppScan\;C:\Program Files\MinGW\bin;C:\Program Files\StormII\Codec;C:\Program Files\StormII;C:\Program Files\UltraEdit\
jxe.current.romimage.version => 9
com.ibm.oti.vm.bootstrap.library.path => C:\Program Files\Java_ibm1.5\jre\bin
com.ibm.cpu.endian => little
java.specification.name => Java Platform API Specification
java.class.version => 49.0
ibm.system.encoding => GB18030
java.util.prefs.PreferencesFactory => java.util.prefs.WindowsPreferencesFactory
invokedviajava =>
os.version => 5.1 build 2600 Service Pack 3
com.ibm.oti.vm.library.version => 23
user.home => C:\Documents and Settings\Administrator
user.timezone =>
java.awt.printerjob => sun.awt.windows.WPrinterJob
file.encoding => GBK
java.specification.version => 1.5
user.name => Administrator
java.class.path => C:\Documents and Settings\Administrator\My Documents\workspace\PrintTest\bin
java.vm.specification.version => 1.0
sun.arch.data.model => 32
java.home => C:\Program Files\Java_ibm1.5\jre
com.ibm.oti.jcl.build => 20060331_1751
user.language => zh
ibm.signalhandling.sigint => true
java.specification.vendor => Sun Microsystems Inc.
os.encoding => GB18030
awt.toolkit => sun.awt.windows.WToolkit
java.vm.info => J2RE 1.5.0 IBM J9 2.3 Windows XP x86-32 j9vmwi3223-20060504 (JIT enabled)
J9VM - 20060501_06428_lHdSMR
JIT - 20060428_1800_r8
GC - 20060501_AA
java.version => 1.5.0
java.ext.dirs => C:\Program Files\Java_ibm1.5\jre\lib\ext
jxe.lowest.romimage.version => 9
sun.boot.class.path => C:\Program Files\Java_ibm1.5\jre\lib\FooAttach.jar;C:\Program Files\Java_ibm1.5\jre\lib\vm.jar;C:\Program Files\Java_ibm1.5\jre\lib\core.jar;C:\Program Files\Java_ibm1.5\jre\lib\charsets.jar;C:\Program Files\Java_ibm1.5\jre\lib\graphics.jar;C:\Program Files\Java_ibm1.5\jre\lib\security.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmpkcs.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorb.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcfw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorbapi.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjcefw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjgssprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjsseprovider2.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaaslm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaasactivelm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcertpathprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\server.jar;C:\Program Files\Java_ibm1.5\jre\lib\xml.jar
java.vendor => IBM Corporation
file.separator => \
java.compiler => j9jit23
sun.io.unicode.encoding => UnicodeLittle
ibm.signalhandling.sigchain => true

 

          其中的 sun.boot.class.path 系统属性给出了当前配置的所有 boot class path,这些 jar 包会由 Bootstrap class loader 负责将其中的类装入 JVM。

          截止到此,我们最开始的需求“基本”已经可以达到了,即在不改变应用代码中对 System.out.println 方法调用前提下,通过 hack JRE 实现改变了 System.out.println 实现。但所谓“基本”是由于我们需要配置 -Xbootclasspath 选项来让 bootstrap class loader 加载我们封装的自定义代码 jar 文件。那么是否也可以通过上面类似的 hack 方法来更新 JVM 的默认 boot class path 值呢?经过分析认为不可能,因为 boot class path 的内容,也就是 bootstrap class loader 负责加载的默认 jar 文件集是 JVM 在源代码中/编译前指定的,而在 JRE 层面完成该配置的方法只能通过 Java 进程的 -Xbootclasspath 命令行参数来完成。

          下面的分析过程将针对 JVM 实现展开,目标是以 openjdk 开源项目为基础,源码可以在这里下载:
          openjdk-7-ea-src-b59-14_may_2009

 

          首先,openjdk\hotspot\src\share\vm\runtime\os.cpp 文件 bool os::set_boot_path(char fileSep, char pathSep) 方法指出了 bootstrap class loader 负责加载的默认 jar 文件集:

 

bool os::set_boot_path(char fileSep, char pathSep) {
    const char* home = Arguments::get_java_home();
    int home_len = (int)strlen(home);

    static const char* meta_index_dir_format = "%/lib/";
    static const char* meta_index_format = "%/lib/meta-index";
    char* meta_index = format_boot_path(meta_index_format, home, home_len, fileSep, pathSep);
    if (meta_index == NULL) return false;
    char* meta_index_dir = format_boot_path(meta_index_dir_format, home, home_len, fileSep, pathSep);
    if (meta_index_dir == NULL) return false;
    Arguments::set_meta_index_path(meta_index, meta_index_dir);

    // Any modification to the JAR-file list, for the boot classpath must be
    // aligned with install/install/make/common/Pack.gmk. Note: boot class
    // path class JARs, are stripped for StackMapTable to reduce download size.
    static const char classpath_format[] =
        "%/lib/resources.jar:"
        "%/lib/rt.jar:"
        "%/lib/sunrsasign.jar:"
        "%/lib/jsse.jar:"
        "%/lib/jce.jar:"
        "%/lib/charsets.jar:"
        "%/classes";
    char* sysclasspath = format_boot_path(classpath_format, home, home_len, fileSep, pathSep);
    if (sysclasspath == NULL) return false;
    Arguments::set_sysclasspath(sysclasspath);

    return true;
}

 

          然后,在 openjdk\hotspot\src\share\vm\runtime\arguments.hpp 文件中定义了 Arguments::set_sysclasspath 方法,它将参数给出的 jar 包文件路径存入 Arguments::_sun_boot_class_path 的 SystemProperty 结构中:

 

static void Arguments::set_sysclasspath(char *value) { _sun_boot_class_path->set_value(value); }

static SystemProperty *_sun_boot_class_path;

 

          其次,我们通过 Java 进程的 -Xbootclasspath 命令行参数配置的 boot class jar 包路径会通过 openjdk\hotspot\src\share\vm\prims\jvmtiEnv.cpp 文件中定义的 AddToBootstrapClassLoaderSearch 方法添加到上述的 _sun_boot_class_path SystemProperty 结构中。其中也是通过调用 Arguments::append_sysclasspath 方法来完成的:

 

JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) {
  jvmtiPhase phase = get_phase();
  if (phase == JVMTI_PHASE_ONLOAD) {
    Arguments::append_sysclasspath(segment);
    return JVMTI_ERROR_NONE;
  } else {
    assert(phase == JVMTI_PHASE_LIVE, "sanity check");

    // create the zip entry
    ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment);
    if (zip_entry == NULL) {
      return JVMTI_ERROR_ILLEGAL_ARGUMENT;
    }

    // lock the loader
    Thread* thread = Thread::current();
    HandleMark hm;
    Handle loader_lock = Handle(thread, SystemDictionary::system_loader_lock());

    ObjectLocker ol(loader_lock, thread);

    // add the jar file to the bootclasspath
    if (TraceClassLoading) {
      tty->print_cr("[Opened %s]", zip_entry->name());
    }
    ClassLoader::add_to_list(zip_entry);
    return JVMTI_ERROR_NONE;
  }

}

 

<function id="AddToBootstrapClassLoaderSearch" jkernel="yes" phase="onload" num="149">
	<synopsis>Add To Bootstrap Class Loader Search</synopsis>
	<description>
		This function can be used to cause instrumentation classes to be defined by the
		bootstrap class loader. See
		<vmspeclink id="ConstantPool.doc.html#79383"
			name="Loading Using the Bootstrap Class Loader"
		preposition="in"/>.
		After the bootstrap
		class loader unsuccessfully searches for a class, the specified platform-dependent
	search path <paramlink id="segment"/> will be searched as well. Only one segment may be specified in
the <paramlink id="segment"/>. This function may be called multiple times to add multiple segments,
the segments will be searched in the order that this function was called.
<p/>
In the <code>OnLoad</code> phase the function may be used to specify any platform-dependent
search path segment to be searched after the bootstrap class loader unsuccessfully searches
for a class. The segment is typically a directory or JAR file.
<p/>
In the live phase the <paramlink id="segment"/> may be used to specify any platform-dependent
path to a <externallink id="http://java.sun.com/javase/6/docs/guide/jar/jar.html">
JAR file</externallink>. The agent should take care that the JAR file does not
contain any classes or resources other than those to be defined by the bootstrap
class loader for the purposes of instrumentation.
<p/>
The <vmspeclink/> specifies that a subsequent attempt to resolve a symbolic
reference that the Java virtual machine has previously unsuccessfully attempted
to resolve always fails with the same error that was thrown as a result of the
initial resolution attempt. Consequently, if the JAR file contains an entry
that corresponds to a class for which the Java virtual machine has
unsuccessfully attempted to resolve a reference, then subsequent attempts to
resolve that reference will fail with the same error as the initial attempt.
</description>
<origin>new</origin>
<capabilities>
</capabilities>
<parameters>
	<param id="segment">
		<inbuf>
			<char/>
		</inbuf>
		<description>
			The platform-dependent search path segment, encoded as a
			<internallink id="mUTF">modified UTF-8</internallink> string.
		</description>
	</param>
</parameters>
<errors>
	<error id="JVMTI_ERROR_ILLEGAL_ARGUMENT">
	<paramlink id="segment"/> is an invalid path. In the live phase, anything other than an
	existing JAR file is an invalid path.
</error>
</errors>
</function>

 

          Arguments::append_sysclasspath 方法被定义为:

 

static void Arguments::append_sysclasspath(const char *value) {
  _sun_boot_class_path->append_value(value);
}

 

          说明最终 bootstrap class loader 负责加载的 boot class jar 文件路径都被保存在 _sun_boot_class_path 中。

          接下来,openjdk\hotspot\src\share\vm\classfile\classLoader.cpp 文件中定义了 ClassLoader 类,它将 Arguments::_sun_boot_class_path 中指定的 jar 文件路径初使化,并保存在 ClassPathEntry 链表中:

 

void ClassLoader::setup_bootstrap_search_path() {
  assert(_first_entry == NULL, "should not setup bootstrap class search path twice");
  char* sys_class_path = os::strdup(Arguments::get_sysclasspath());
  if (TraceClassLoading && Verbose) {
    tty->print_cr("[Bootstrap loader class path=%s]", sys_class_path);
  }

  int len = (int)strlen(sys_class_path);
  int end = 0;

  // Iterate over class path entries
  for (int start = 0; start < len; start = end) {
    while (sys_class_path[end] && sys_class_path[end] != os::path_separator()[0]) {
      end++;
    }
    char* path = NEW_C_HEAP_ARRAY(char, end-start+1);
    strncpy(path, &sys_class_path[start], end-start);
    path[end-start] = '\0';
    update_class_path_entry_list(path, false);
    FREE_C_HEAP_ARRAY(char, path);
    while (sys_class_path[end] == os::path_separator()[0]) {
      end++;
    }
  }
}

 

ClassPathEntry* ClassLoader::_first_entry = NULL;
ClassPathEntry* ClassLoader::_last_entry = NULL;

 

          最后,最终的 class 加载动作是由 ClassLoader::load_classfile 方法完成的,位于 openjdk\hotspot\src\share\vm\classfile\classLoader.cpp 文件:

 

instanceKlassHandle ClassLoader::load_classfile(symbolHandle h_name, TRAPS) {
  VTuneClassLoadMarker clm;
  ResourceMark rm(THREAD);
  EventMark m("loading class " INTPTR_FORMAT, (address)h_name());
  ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);

  stringStream st;
  // st.print() uses too much stack space while handling a StackOverflowError
  // st.print("%s.class", h_name->as_utf8());
  st.print_raw(h_name->as_utf8());
  st.print_raw(".class");
  char* name = st.as_string();

  // Lookup stream for parsing .class file
  ClassFileStream* stream = NULL;
  int classpath_index = 0;
  {
    PerfTraceTime vmtimer(perf_accumulated_time());
    ClassPathEntry* e = _first_entry;
    while (e != NULL) {
      stream = e->open_stream(name);
      if (stream != NULL) {
        break;
      }
      e = e->next();
      ++classpath_index;
    }
  }

  instanceKlassHandle h(THREAD, klassOop(NULL));
  if (stream != NULL) {

    // class file found, parse it
    ClassFileParser parser(stream);
    Handle class_loader;
    Handle protection_domain;
    symbolHandle parsed_name;
    instanceKlassHandle result = parser.parseClassFile(h_name,
                                                       class_loader,
                                                       protection_domain,
                                                       parsed_name,
                                                       CHECK_(h));

    // add to package table
    if (add_package(name, classpath_index, THREAD)) {
      h = result;
    }
  }

  return h;
}

 

          从上面的分析可以看出,若需要改变 JRE 默认 boot classpath 配置,加入自定的 jar 文件,则需要更改 openjdk\hotspot\src\share\vm\runtime\os.cpp 文件,并重新编译 jvm,而且如果 boot classpath 包含的默认 jar 文件过多、容量增大,还会影响JVM的加/下载、启动时间。

 

          希望通过本次对 JRE 无奈的 hack 过程所做的浅显研究,能够在后续有机会做更深入的学习、研究、收获。

 

作者:lzy.je
出处:http://lzy.iteye.com
本文版权归作者所有,只允许以摘要和完整全文两种形式转载,不允许对文字进行裁剪。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


// 2009.06.14 01:10 添加 ////

          推荐一篇比较详细介绍 bootstrap class loader 和 boot classpath 和相关内容的文章,篇幅不长值得快速一览。参见附件 BootClasspath.pdf

 

// 2009.08.18 16:04 添加 ////

 

          在编译 Hack/替换的代码时,一定要注意编译器(javac)版本代码版本兼容性生成类文件的虚拟机版本 3 件事。否则编译虽然能够成功,但在 Hack/替换后会出现“java.lang.UnsupportedClassVersionError”异常。在 IBM JDK 提供的 javac 编译器上,可以通过“-source <版本>”配置代码版本兼容性,通过“-target <版本>”生成指定虚拟机版本的类文件。

 

  • 大小: 23.5 KB
  • 大小: 25.7 KB
13
2
分享到:
评论
4 楼 lzy.je 2009-06-17  
whitesock 写道

你是否测试过如果Log4j的代码中如果调用了System.out.println或者System.err.println方法后会是什么后果? (log4j的LogLog类的部分方法里就有这些调用)


OK。递归的的确问题需要考虑,但可以通过在println方法中加判断解决。
3 楼 whitesock 2009-06-17  
你是否测试过如果Log4j的代码中如果调用了System.out.println或者System.err.println方法后会是什么后果? (log4j的LogLog类的部分方法里就有这些调用)
2 楼 lzy.je 2009-06-16  
mxswl 写道

不难理解,不过总觉得有点无聊


这是实际需要。至少讨论了一种可能的方式。
1 楼 mxswl 2009-06-16  
不难理解,不过总觉得有点无聊

相关推荐

    JRE瘦身 jre减肥 精简jre jre精简

    因为很多用户的电脑上没有合适版本的jre。用一个软件的同时必须安装另外一个软件(jre)会给人一种流氓软件强行捆绑的感觉,而很多用户对此很抵触。更不要提用户电脑上的jre版本千变万化(有没有jre,是微软的还是...

    精简jre步骤 迷你jre制作过程

    精简jre步骤 1. 拷贝一个完整版的jre文件夹到D盘 2. 删除jre目录下所有出bin和lib目录的所有文件或目录 3. 打开cmd窗口,设置path路径为空,转到D:\jre\bin目录,运行java –version。正常显示当前Java的版本。 4. ...

    jre-8u241-linux-i586.tar.gz

    JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8....

    jre-8u301.zip

    (1)jre-8u301-linux-i586.rpm (2)jre-8u301-linux-i586.tar.gz (3)jre-8u301-linux-x64.rpm (4)jre-8u301-linux-x64.tar.gz (5)jre-8u301-macosx-x64.dmg (6)jre-8u301-macosx-x64.tar.gz (7)jre-8u...

    JRE1.6 Win32 X86

    Java运行环境(Java Runtime Environment,简称JRE)是运行Java应用程序所必需的系统组件,它提供了执行Java字节码的虚拟机以及相关的类库。在本案例中,我们讨论的是JRE1.6的Win32版本,适用于32位Windows操作系统...

    jre1.6.25_32位

    Java运行环境(JRE,Java Runtime Environment)是Java应用程序执行所需的基础组件,它包含了Java虚拟机(JVM)、Java核心类库以及其他支持Java程序运行的必要组件。在本例中,我们关注的是JRE 1.6的32位版本,具体...

    jre6 java运行环境免安装版

    Java运行环境(JRE)是执行Java应用程序必不可少的组件,JRE6,即Java Runtime Environment 1.6,是Oracle公司发布的Java平台的一个版本。这个版本在2006年推出,提供了许多新特性和改进,以提升性能、稳定性和安全...

    JRE7-32bit

    Java运行环境(JRE,Java Runtime Environment)是Java应用程序运行的必备组件,它包含了Java虚拟机(JVM,Java Virtual Machine)以及Java基础类库。标题中的“JRE7-32bit”指的是针对32位操作系统的Java运行环境第...

    android studio3.0.1的jre包替换

    2. **下载新JRE**:寻找一个适合的JRE版本,可以是Oracle官方发布的JRE,也可以是OpenJDK的版本,确保该版本与你的操作系统兼容,并且对Android Studio有良好的支持。 3. **替换JRE**:将下载的JRE解压后,覆盖到...

    java的jre1.5

    Java的JRE1.5,全称为Java Runtime Environment 1.5,是Sun Microsystems公司(后被Oracle收购)推出的一款重要版本的Java运行环境。它为用户提供了执行Java应用程序和Applet所需的所有组件,确保了网页上的Java内容...

    jre-8u191-windows-x64.exe,jre最后一个免费版本.rar

    Java运行环境(JRE,Java Runtime Environment)是执行Java应用程序必不可少的组件,它包含了Java虚拟机(JVM,Java Virtual Machine)以及Java平台标准版(Java SE,Java Platform, Standard Edition)所需的类库和...

    jre8-windows64版本下载安装

    Java运行环境(JRE,Java Runtime Environment)是执行Java应用程序所必需的基础软件,它包含了Java虚拟机(JVM,Java Virtual Machine)以及Java类库。`jre8-windows64版本下载安装`这个主题主要涉及在Windows 64位...

    JRE 6 Java SE Runtime Environment 6u45

    JRE6 , Java SE 运行时环境 6u45 ,Linux/windows系统下 32位/64位 安装包,包含以下版本: Linux x86 jre-6u45-linux-i586-rpm.bin  Linux x86 jre-6u45-linux-i586.bin  Linux x64 jre-6u45-linux-x64-rpm.bin ...

    jre1.8.0_161

    Java运行环境(JRE,Java Runtime Environment)是Java应用程序运行的必备组件,它包含了Java虚拟机(JVM,Java Virtual Machine)以及Java基础类库。标题中的"jre1.8.0_161"指的是Java运行环境的特定版本,1.8.0...

    jre9下载 jdk9

    Java运行环境(JRE)是执行Java应用程序必不可少的组件,而JRE 9.0是Oracle公司发布的一个重要版本,其包含了许多改进和新特性。本文将深入探讨JRE 9及其下载、安装过程,以及它对Java开发的影响。 首先,JRE 9.0的...

    瘦身JRE让其变成 5M左右

    "瘦身JRE让其变成5M左右"这个过程,就是针对Java运行时环境(JRE)进行优化,减少其体积,以适应有限存储空间的需求。以下是详细的知识点解析: 1. **JRE精减**:JRE包含了Java运行所需的核心库和虚拟机,通常占用...

    jre 1.8.45 下载

    Java运行环境(JRE,Java Runtime Environment)是Java应用程序运行的必备组件,它包含了Java虚拟机(JVM,Java Virtual Machine)以及Java基础类库。JRE 1.8.45是Oracle公司发布的一个特定版本,对于这个版本,我们...

    JRE 7 Java SE Runtime Environment 7u80

    JRE 7, Java SE 运行时环境 7u80,Linux/windows/Mac OS系统下 32位/64位 安装包,包含以下版本: Linux x86 jre-7u80-linux-i586.rpm Linux x86 jre-7u80-linux-i586.tar.gz Linux x64 jre-7u80-linux-x64.rpm ...

    JRE1.8.0-391

    Java运行环境(JRE,Java Runtime Environment)是Java应用程序运行的基础,它包含了Java虚拟机(JVM,Java Virtual Machine)和Java平台标准版(Java SE,Java Platform, Standard Edition)的类库。JRE1.8.0_391是...

Global site tag (gtag.js) - Google Analytics