当前位置: 首页 > 编程日记 > 正文

JDK源码研究Jstack,JMap,threaddump,dumpheap的原理

JDK最新bug和任务领取:https://bugs.openjdk.java.net/projects/JDK/issues

参加OpenJDK社区:https://bugs.openjdk.java.net/projects/JDK/issues

openjdk源码地址:

https://jdk.java.net/java-se-ri/8

https://download.java.net/openjdk/jdk8u40/ri/openjdk-8u40-src-b25-10_feb_2015.zip

如果国外网速不行,这里有下好放csdn上的: JDK源码 openjdk-8u40-src-b25-10_feb_2015.zip

线上源码:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/6e2900603bc6/

如果官网很慢,可以直接CSDN下载:https://download.csdn.net/download/21aspnet/11028742

JEP 0:JEP指数:http://openjdk.java.net/jeps/0

Java8官方文档总目录

Java8语言规范      Java8虚拟机规范     HotSpot虚拟机垃圾收集调整指南

Java8 API

--------------

https://jdk.java.net/java-se-ri/12

https://download.java.net/openjdk/jdk12/ri/openjdk-12+32_src.zip

----------------

Java语言和虚拟机规范[各语言总目录]

其他jdk文档地址:https://docs.oracle.com/en/java/javase/index.html

----

扩展阅读:虽然是历史资源,但是还是闪烁着智慧的

Oracle JRockit文档    Oracle JRockit在线文档   【有参考价值】

Oracle JRockit联机文档库4.0版   【很有价值】

----

Java12【总目录】

Java™教程【有参考价值】:

学习Java语言   异常   基本I/O    并发  泛型  反射  集合  序列化

Lambda表达式  聚合操作

垃圾收集调整【重要】    Java虚拟机指南【很重要】    JRockit到HotSpot迁移指南【有参考价值】

故障排除指南【重要】

https://docs.oracle.com/en/java/javase/11/   和12类似

----

分析Jstack源码

这是起点>>>

\openjdk\jdk\src\share\classes\sun\tools目录下

常见的jvm命令jstack  jmap   jps都在这里

package sun.tools.jstack;import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.io.IOException;
import java.io.InputStream;import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.AttachNotSupportedException;
import sun.tools.attach.HotSpotVirtualMachine;/** This class is the main class for the JStack utility. It parses its arguments* and decides if the command should be executed by the SA JStack tool or by* obtained the thread dump from a target process using the VM attach mechanism*/
public class JStack {public static void main(String[] args) throws Exception {if (args.length == 0) {usage(1); // no arguments}boolean useSA = false;boolean mixed = false;boolean locks = false;// Parse the options (arguments starting with "-" )int optionCount = 0;while (optionCount < args.length) {String arg = args[optionCount];if (!arg.startsWith("-")) {break;}if (arg.equals("-help") || arg.equals("-h")) {usage(0);}else if (arg.equals("-F")) {useSA = true;}else {if (arg.equals("-m")) {mixed = true;} else {if (arg.equals("-l")) {locks = true;} else {usage(1);}}}optionCount++;}// mixed stack implies SA toolif (mixed) {useSA = true;}// Next we check the parameter count. If there are two parameters// we assume core file and executable so we use SA.int paramCount = args.length - optionCount;if (paramCount == 0 || paramCount > 2) {usage(1);}if (paramCount == 2) {useSA = true;} else {// If we can't parse it as a pid then it must be debug serverif (!args[optionCount].matches("[0-9]+")) {useSA = true;}}// now execute using the SA JStack tool or the built-in thread dumperif (useSA) {// parameters (<pid> or <exe> <core>String params[] = new String[paramCount];for (int i=optionCount; i<args.length; i++ ){params[i-optionCount] = args[i];}runJStackTool(mixed, locks, params);} else {// pass -l to thread dump operation to get extra lock infoString pid = args[optionCount];String params[];if (locks) {params = new String[] { "-l" };} else {params = new String[0];}runThreadDump(pid, params);}}

根据传入参数的不同,有两种实现机制,一种是基于SA,一种是通过attach。

下面是jmap部分代码下面是用的最多的:

 // Invoke SA tool  with the given argumentsprivate static void runTool(String option, String args[]) throws Exception {String[][] tools = {{ "-pmap",          "sun.jvm.hotspot.tools.PMap"             },{ "-heap",          "sun.jvm.hotspot.tools.HeapSummary"      },{ "-heap:format=b", "sun.jvm.hotspot.tools.HeapDumper"       },{ "-histo",         "sun.jvm.hotspot.tools.ObjectHistogram"  },{ "-clstats",       "sun.jvm.hotspot.tools.ClassLoaderStats" },{ "-finalizerinfo", "sun.jvm.hotspot.tools.FinalizerInfo"    },};

-------------------

都是通过 executeCommand 来实现的,例如:datadump、threaddump、dumpheap、inspectheap、jcmd等,而最终的execute()在Linux上是由类LinuxVirtualMachine来完成。

public abstract class HotSpotVirtualMachine extends VirtualMachine {
...// --- HotSpot specific methods ---// same as SIGQUITpublic void localDataDump() throws IOException {executeCommand("datadump").close();}// Remote ctrl-break. The output of the ctrl-break actions can// be read from the input stream.public InputStream remoteDataDump(Object ... args) throws IOException {return executeCommand("threaddump", args);}// Remote heap dump. The output (error message) can be read from the// returned input stream.public InputStream dumpHeap(Object ... args) throws IOException {return executeCommand("dumpheap", args);}// Heap histogram (heap inspection in HotSpot)public InputStream heapHisto(Object ... args) throws IOException {return executeCommand("inspectheap", args);}// set JVM command line flagpublic InputStream setFlag(String name, String value) throws IOException {return executeCommand("setflag", name, value);}// print command line flagpublic InputStream printFlag(String name) throws IOException {return executeCommand("printflag", name);}public InputStream executeJCmd(String command) throws IOException {return executeCommand("jcmd", command);}// -- Supporting methods

-----------------------------------

jstack命令首先会attach到目标jvm进程,产生VirtualMachine类;

linux系统下,其实现类为LinuxVirtualMachine,调用其remoteDataDump方法,打印堆栈信息;

VirtualMachine是如何连接到目标JVM进程的呢?
具体的实现逻辑在sun.tools.attach.LinuxVirtualMachine的构造函数:

    // The patch to the socket file created by the target VMString path;/*** Attaches to the target VM*/LinuxVirtualMachine(AttachProvider provider, String vmid)throws AttachNotSupportedException, IOException{super(provider, vmid);// This provider only understands pidsint pid;try {pid = Integer.parseInt(vmid);} catch (NumberFormatException x) {throw new AttachNotSupportedException("Invalid process identifier");}// Find the socket file. If not found then we attempt to start the// attach mechanism in the target VM by sending it a QUIT signal.// Then we attempt to find the socket file again.path = findSocketFile(pid);if (path == null) {File f = createAttachFile(pid);try {// On LinuxThreads each thread is a process and we don't have the// pid of the VMThread which has SIGQUIT unblocked. To workaround// this we get the pid of the "manager thread" that is created// by the first call to pthread_create. This is parent of all// threads (except the initial thread).if (isLinuxThreads) {int mpid;try {mpid = getLinuxThreadsManager(pid);} catch (IOException x) {throw new AttachNotSupportedException(x.getMessage());}assert(mpid >= 1);sendQuitToChildrenOf(mpid);} else {sendQuitTo(pid);}// give the target VM time to start the attach mechanismint i = 0;long delay = 200;int retries = (int)(attachTimeout() / delay);do {try {Thread.sleep(delay);} catch (InterruptedException x) { }path = findSocketFile(pid);i++;} while (i <= retries && path == null);if (path == null) {throw new AttachNotSupportedException("Unable to open socket file: target process not responding " +"or HotSpot VM not loaded");}} finally {f.delete();}}// Check that the file owner/permission to avoid attaching to// bogus processcheckPermissions(path);// Check that we can connect to the process// - this ensures we throw the permission denied error now rather than// later when we attempt to enqueue a command.int s = socket();try {connect(s, path);} finally {close(s);}}/*** Detach from the target VM*/public void detach() throws IOException {synchronized (this) {if (this.path != null) {this.path = null;}}}
  • 查找/tmp目录下是否存在".java_pid"+pid文件;
  • 如果文件不存在,则首先创建"/proc/" + pid + "/cwd/" + ".attach_pid" + pid文件,然后通过kill命令发送SIGQUIT信号给目标JVM进程;
  • 目标JVM进程接收到信号之后,会在/tmp目录下创建".java_pid"+pid文件
  • 当发现/tmp目录下存在".java_pid"+pid文件,LinuxVirtualMachine会通过connect系统调用连接到该文件描述符,后续通过该fd进行双方的通讯;

JVM接受SIGQUIT信号的相关逻辑在os.cpp文件的os::signal_init方法:

jstack是通过调用remoteDataDump方法实现的,该方法就是通过往前面提到的fd中写入threaddump指令,读取返回结果,从而得到目标JVM的堆栈信息。

----------------------------------

jstack等命令会与jvm进程建立socket连接,发送对应的指令(jstack发送了threaddump指令),然后再读取返回的数据。

/*** Execute the given command in the target VM.*/InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException {assert args.length <= 3;                // includes null// did we detach?String p;synchronized (this) {if (this.path == null) {throw new IOException("Detached from target VM");}p = this.path;}// create UNIX socketint s = socket();// connect to target VMtry {connect(s, p);} catch (IOException x) {close(s);throw x;}IOException ioe = null;// connected - write request// <ver> <cmd> <args...>try {writeString(s, PROTOCOL_VERSION);writeString(s, cmd);for (int i=0; i<3; i++) {if (i < args.length && args[i] != null) {writeString(s, (String)args[i]);} else {writeString(s, "");}}} catch (IOException x) {ioe = x;}// Create an input stream to read replySocketInputStream sis = new SocketInputStream(s);// Read the command completion statusint completionStatus;try {completionStatus = readInt(sis);} catch (IOException x) {sis.close();if (ioe != null) {throw ioe;} else {throw x;}}if (completionStatus != 0) {// read from the stream and use that as the error messageString message = readErrorMessage(sis);sis.close();// In the event of a protocol mismatch then the target VM// returns a known error so that we can throw a reasonable// error.if (completionStatus == ATTACH_ERROR_BADVERSION) {throw new IOException("Protocol mismatch with target VM");}// Special-case the "load" command so that the right exception is// thrown.if (cmd.equals("load")) {throw new AgentLoadException("Failed to load agent library");} else {if (message == null) {throw new AttachOperationFailedException("Command failed in target VM");} else {throw new AttachOperationFailedException(message);}}}// Return the input stream so that the command output can be readreturn sis;}

-----------------

下面是C++部分

\openjdk\hotspot\src\share\vm\services\attachListener.hpp

// Table to map operation names to functions.// names must be of length <= AttachOperation::name_length_max
static AttachOperationFunctionInfo funcs[] = {{ "agentProperties",  get_agent_properties },{ "datadump",         data_dump },{ "dumpheap",         dump_heap },{ "load",             JvmtiExport::load_agent_library },{ "properties",       get_system_properties },{ "threaddump",       thread_dump },{ "inspectheap",      heap_inspection },{ "setflag",          set_flag },{ "printflag",        print_flag },{ "jcmd",             jcmd },{ NULL,               NULL }
};

\openjdk\hotspot\src\os\linux\vm\attachListener_linux.cpp

侦听socket

// The attach mechanism on Linux uses a UNIX domain socket. An attach listener
// thread is created at startup or is created on-demand via a signal from
// the client tool. The attach listener creates a socket and binds it to a file
// in the filesystem. The attach listener then acts as a simple (single-
// threaded) server - it waits for a client to connect, reads the request,
// executes it, and returns the response to the client via the socket
// connection.
//
// As the socket is a UNIX domain socket it means that only clients on the
// local machine can connect. In addition there are two other aspects to
// the security:
// 1. The well known file that the socket is bound to has permission 400
// 2. When a client connect, the SO_PEERCRED socket option is used to
//    obtain the credentials of client. We check that the effective uid
//    of the client matches this process.....
// Initialization - create a listener socket and bind it to a fileint LinuxAttachListener::init() {char path[UNIX_PATH_MAX];          // socket filechar initial_path[UNIX_PATH_MAX];  // socket file during setupint listener;                      // listener socket (file descriptor)// register function to cleanup::atexit(listener_cleanup);int n = snprintf(path, UNIX_PATH_MAX, "%s/.java_pid%d",os::get_temp_directory(), os::current_process_id());if (n < (int)UNIX_PATH_MAX) {n = snprintf(initial_path, UNIX_PATH_MAX, "%s.tmp", path);}if (n >= (int)UNIX_PATH_MAX) {return -1;}// create the listener socketlistener = ::socket(PF_UNIX, SOCK_STREAM, 0);if (listener == -1) {return -1;}// bind socketstruct sockaddr_un addr;addr.sun_family = AF_UNIX;strcpy(addr.sun_path, initial_path);::unlink(initial_path);int res = ::bind(listener, (struct sockaddr*)&addr, sizeof(addr));if (res == -1) {::close(listener);return -1;}// put in listen mode, set permissions, and rename into placeres = ::listen(listener, 5);if (res == 0) {RESTARTABLE(::chmod(initial_path, S_IREAD|S_IWRITE), res);if (res == 0) {res = ::rename(initial_path, path);}}if (res == -1) {::close(listener);::unlink(initial_path);return -1;}set_path(path);set_listener(listener);return 0;
}
....

再就是一个个命令对应去看具体代码实现,以dumpheap为例:

\openjdk\hotspot\src\share\vm\services\heapDumper.cpp

// The VM operation that dumps the heap. The dump consists of the following
// records:
//
//  HPROF_HEADER
//  [HPROF_UTF8]*
//  [HPROF_LOAD_CLASS]*
//  [[HPROF_FRAME]*|HPROF_TRACE]*
//  [HPROF_GC_CLASS_DUMP]*
//  HPROF_HEAP_DUMP
//
// The HPROF_TRACE records represent the stack traces where the heap dump
// is generated and a "dummy trace" record which does not include
// any frames. The dummy trace record is used to be referenced as the
// unknown object alloc site.
//
// The HPROF_HEAP_DUMP record has a length following by sub-records. To allow
// the heap dump be generated in a single pass we remember the position of
// the dump length and fix it up after all sub-records have been written.
// To generate the sub-records we iterate over the heap, writing
// HPROF_GC_INSTANCE_DUMP, HPROF_GC_OBJ_ARRAY_DUMP, and HPROF_GC_PRIM_ARRAY_DUMP
// records as we go. Once that is done we write records for some of the GC
// roots.// HPROF_TRACE记录表示堆转储的堆栈跟踪
//生成并且“虚拟跟踪”记录不包括
//任何帧 虚拟跟踪记录用于引用
//未知对象分配站点。
//
// HPROF_HEAP_DUMP记录的子记录长度如下。 允许
//在一次传递中生成堆转储,我们记住了它的位置
//转储长度并在写完所有子记录后修复它。
//为了生成子记录,我们迭代堆,写
// HPROF_GC_INSTANCE_DUMP,HPROF_GC_OBJ_ARRAY_DUMP和HPROF_GC_PRIM_ARRAY_DUMP
//我们去的记录。 完成后,我们会为某些GC编写记录
//根
void VM_HeapDumper::doit() {HandleMark hm;CollectedHeap* ch = Universe::heap();ch->ensure_parsability(false); // must happen, even if collection does// not happen (e.g. due to GC_locker)if (_gc_before_heap_dump) {if (GC_locker::is_active()) {warning("GC locker is held; pre-heapdump GC was skipped");} else {ch->collect_as_vm_thread(GCCause::_heap_dump);}}// At this point we should be the only dumper active, so// the following should be safe.set_global_dumper();set_global_writer();// Write the file header - use 1.0.2 for large heaps, otherwise 1.0.1size_t used = ch->used();const char* header;if (used > (size_t)SegmentedHeapDumpThreshold) {set_segmented_dump();header = "JAVA PROFILE 1.0.2";} else {header = "JAVA PROFILE 1.0.1";}// header is few bytes long - no chance to overflow intwriter()->write_raw((void*)header, (int)strlen(header));writer()->write_u1(0); // terminatorwriter()->write_u4(oopSize);writer()->write_u8(os::javaTimeMillis());// HPROF_UTF8 recordsSymbolTableDumper sym_dumper(writer());SymbolTable::symbols_do(&sym_dumper);// write HPROF_LOAD_CLASS recordsClassLoaderDataGraph::classes_do(&do_load_class);Universe::basic_type_classes_do(&do_load_class);// write HPROF_FRAME and HPROF_TRACE records// this must be called after _klass_map is built when iterating the classes above.dump_stack_traces();// write HPROF_HEAP_DUMP or HPROF_HEAP_DUMP_SEGMENTwrite_dump_header();// Writes HPROF_GC_CLASS_DUMP recordsClassLoaderDataGraph::classes_do(&do_class_dump);Universe::basic_type_classes_do(&do_basic_type_array_class_dump);check_segment_length();// writes HPROF_GC_INSTANCE_DUMP records.// After each sub-record is written check_segment_length will be invoked. When// generated a segmented heap dump this allows us to check if the current// segment exceeds a threshold and if so, then a new segment is started.// The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk// of the heap dump.HeapObjectDumper obj_dumper(this, writer());Universe::heap()->safe_object_iterate(&obj_dumper);// HPROF_GC_ROOT_THREAD_OBJ + frames + jni localsdo_threads();check_segment_length();// HPROF_GC_ROOT_MONITOR_USEDMonitorUsedDumper mon_dumper(writer());ObjectSynchronizer::oops_do(&mon_dumper);check_segment_length();// HPROF_GC_ROOT_JNI_GLOBALJNIGlobalsDumper jni_dumper(writer());JNIHandles::oops_do(&jni_dumper);check_segment_length();// HPROF_GC_ROOT_STICKY_CLASSStickyClassDumper class_dumper(writer());SystemDictionary::always_strong_classes_do(&class_dumper);// fixes up the length of the dump record. In the case of a segmented// heap then the HPROF_HEAP_DUMP_END record is also written.end_of_dump();// Now we clear the global variables, so that a future dumper might run.clear_global_dumper();clear_global_writer();
}
void VM_HeapDumper::dump_stack_traces() {// write a HPROF_TRACE record without any frames to be referenced as object alloc sitesDumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4));writer()->write_u4((u4) STACK_TRACE_ID);writer()->write_u4(0);                    // thread numberwriter()->write_u4(0);                    // frame count_stack_traces = NEW_C_HEAP_ARRAY(ThreadStackTrace*, Threads::number_of_threads(), mtInternal);int frame_serial_num = 0;for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) {oop threadObj = thread->threadObj();if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) {// dump thread stack traceThreadStackTrace* stack_trace = new ThreadStackTrace(thread, false);stack_trace->dump_stack_at_safepoint(-1);_stack_traces[_num_threads++] = stack_trace;// write HPROF_FRAME records for this thread's stack traceint depth = stack_trace->get_stack_depth();int thread_frame_start = frame_serial_num;int extra_frames = 0;// write fake frame that makes it look like the thread, which caused OOME,// is in the OutOfMemoryError zero-parameter constructorif (thread == _oome_thread && _oome_constructor != NULL) {int oome_serial_num = _klass_map->find(_oome_constructor->method_holder());// the class serial number starts from 1assert(oome_serial_num > 0, "OutOfMemoryError class not found");DumperSupport::dump_stack_frame(writer(), ++frame_serial_num, oome_serial_num,_oome_constructor, 0);extra_frames++;}for (int j=0; j < depth; j++) {StackFrameInfo* frame = stack_trace->stack_frame_at(j);Method* m = frame->method();int class_serial_num = _klass_map->find(m->method_holder());// the class serial number starts from 1assert(class_serial_num > 0, "class not found");DumperSupport::dump_stack_frame(writer(), ++frame_serial_num, class_serial_num, m, frame->bci());}depth += extra_frames;// write HPROF_TRACE record for one threadDumperSupport::write_header(writer(), HPROF_TRACE, 3*sizeof(u4) + depth*oopSize);int stack_serial_num = _num_threads + STACK_TRACE_ID;writer()->write_u4(stack_serial_num);      // stack trace serial numberwriter()->write_u4((u4) _num_threads);     // thread serial numberwriter()->write_u4(depth);                 // frame countfor (int j=1; j <= depth; j++) {writer()->write_id(thread_frame_start + j);}}}
}
// dump the heap to given path.
PRAGMA_FORMAT_NONLITERAL_IGNORED_EXTERNAL
int HeapDumper::dump(const char* path) {assert(path != NULL && strlen(path) > 0, "path missing");// print message in interactive caseif (print_to_tty()) {tty->print_cr("Dumping heap to %s ...", path);timer()->start();}// create the dump writer. If the file can be opened then bailDumpWriter writer(path);if (!writer.is_open()) {set_error(writer.error());if (print_to_tty()) {tty->print_cr("Unable to create %s: %s", path,(error() != NULL) ? error() : "reason unknown");}return -1;}// generate the dumpVM_HeapDumper dumper(&writer, _gc_before_heap_dump, _oome);if (Thread::current()->is_VM_thread()) {assert(SafepointSynchronize::is_at_safepoint(), "Expected to be called at a safepoint");dumper.doit();} else {VMThread::execute(&dumper);}// close dump file and record any error that the writer may have encounteredwriter.close();set_error(writer.error());// print message in interactive caseif (print_to_tty()) {timer()->stop();if (error() == NULL) {char msg[256];sprintf(msg, "Heap dump file created [%s bytes in %3.3f secs]",JLONG_FORMAT, timer()->seconds());
PRAGMA_DIAG_PUSH
PRAGMA_FORMAT_NONLITERAL_IGNORED_INTERNALtty->print_cr(msg, writer.bytes_written());
PRAGMA_DIAG_POP} else {tty->print_cr("Dump file is incomplete: %s", writer.error());}}return (writer.error() == NULL) ? 0 : -1;
}

说明:本文参考了《jstack是如何获取threaddump的》和《java attach机制源码阅读》这两篇都是java部分的缺少C++,然后C++部分是我加上的。

--------------------

《OpenJDK源码学习-加载本地库》

从整个加载本地库的流程来看,基本上还是调用和平台有关的函数来完成的,并在加载和卸载的时候分别调用了两个生命周期回调函数 JNI_OnLoadJNI_OnUnLoad

以linux平台为例,简单总结一下整个so库的加载流程:

  1. 首先 System.loadLibrary() 被调用,开始整个加载过程。
  2. 其中调用 ClassLoader 对象来完成主要工作,将每个本地库封装成 NativeLibrary 对象,并以静态变量存到已经加载过的栈中。
  3. 执行NativeLibrary 类的 load native方法,来交给native层去指向具体的加载工作。
  4. native层 ClassLoader.c 中的 Java_java_lang_ClassLoader_00024NativeLibrary_load 函数被调用。
  5. 在native load函数中首先使用 dlopen 来加载so本地库文件,并将返回的handle保存到 NativeLibrary对象中。
  6. 接着查找已经加载的so本地库中的 JNI_OnLoad 函数,并执行它。
  7. 整个so本地库的加载流程完毕。

只有在 NativeLibrary 对象被GC回收的时候,其 finalize 方法被调用了,对应加载的本地库才会被 unload 。这种情况一般来说并不会发生,因为 NativeLibrary 对象是以静态变量的形式被保存的,而静态变量是 GC roots,一般来说都不会被回收掉的。

相关文章:

C语言中regex_error,为什么这个C 11 std :: regex示例抛出一个regex_error异常?

参见英文答案 >Is gcc 4.8 or earlier buggy about regular expressions? 2尝试学习如何在C 11中使用新的std :: regex.但是我尝试的例子是抛出一个我不明白的regex_error异常.这是我的示例代码&#xff1a;#include #include int main…

如何删除mac通用二进制文件

通用二进制文件是什么&#xff1f; 计算机文件基本上分为二种&#xff1a;二进制文件和 ASCII&#xff08;也称纯文本文件&#xff09;&#xff0c;图形文件及文字处理程序等计算机程序都属于二进制文件。这些文件含有特殊的格式及计算机代码。ASCII 则是可以用任何文字处理程序…

约瑟夫环 猴子选大王

<? /*** 猴子选大王&#xff1a;一群猴子排成一圈&#xff0c;按1,2,…,n依次编号。* 然后从第1只开始数&#xff0c;数到第m只,把它踢出圈&#xff0c;从它后面再开始数&#xff0c;再数到第m只&#xff0c;在把它踢出去…&#xff0c;* 如此不停的进行下去&#xff0c;直…

Java使用字节码和汇编语言同步分析volatile,synchronized的底层实现

关于怎么查看字节码的五种方法参考本人另一篇文章《Java以及IDEA下查看字节码的五种方法》 查看汇编语言汇编码 说要看汇编还是很有必要的&#xff0c;因为有些地方比如加锁其实还是通过汇编实现的&#xff0c;只看字节码不能看出底层实现。 其实就是利用使用hsdis与jitwat…

云计算读书笔记(五)

Hadoop&#xff1a;Google云计算的开源实现 Hadoop是Apache开源组织的一个分布式计算机框架&#xff0c;可以在大量廉价的硬件设备组成的集群上运行应用程序&#xff0c;为应用程序提供一组稳定可靠的接口&#xff0c;旨在构建一个具有高可靠性和良好扩展性的分布式系统。 Hado…

自动跟随小车c语言,基于OpenCV的智能小车运动轨迹跟踪方法研究

摘要&#xff1a;随着人工智能技术的快速发展,智能小车开发受到越来越多研究者的关注,也已经成为一个重要的研究方向,而解决智能小车在路径规划中行驶的运动故障重要手段是对其进行的视频监控,但是智能小车的视频监控只能看到智能小车的行进状况而不进行相对应的处理,所以对智能…

JXJJOI2018_T1_market

题目描述 某天Lemon去超市买柠檬&#xff0c;他发现货架上有N个柠檬&#xff0c;每个柠檬都有一个重量Wi和价格Ci。 Lemon身上只带了S元钱&#xff0c;因此他想要买一个价格不超过S的柠檬回家&#xff0c;另外&#xff0c;他希望他买的那个柠檬的性价比尽量高。 性价比的定义是…

更好的Java虚拟机Zing: 更好的性能,无停顿,更快的启动

Zing虚拟机文档Understanding Java Garbage Collection(了解Java垃圾收集) 首先说明这个Zing是收费的&#xff0c;但是他也是优秀的&#xff0c;我觉得我们可以研究下他的一些思想对于怎么提高JVM&#xff0c;以及目前的JVM有什么缺陷是非常有帮助的。 中文版简介&#xff1a;…

c语言将水仙花数放入一维数组a中,全国计算机等级考试C语言考试程序设计题(13)...

在考生目录下&#xff0c;要求程序PROG.C的功能是&#xff1a;将所有的水仙花数保存到一维数组a中。(所谓水仙花数是指一个三位数&#xff0c;其各位数字立方和等于该数本身。例如&#xff1a;1531*1*15*5*53*3*3)#includevoid main(){void NONO( );//函数声明int a[10]{0},i…

[j2me]类似于OperaMini二级菜单界面演练[1]

拜朋友所赐&#xff0c;今日开始尝试如何绘制类似于Opera Mini的二级菜单&#xff0c;如下图所示&#xff1a;我自己的练习&#xff0c;还很幼稚&#xff0c;姑且记录如下&#xff1a;点击左软键&#xff0c;即可选中界面左下角的“选择”命令&#xff0c;二级菜单旋即弹出&…

宜人贷YEP技术、数据沉淀背后:金融科技迎来开放赋能时代

日前&#xff0c;“IFPI第十届金融科技决策者大会2018”在上海举办&#xff0c;宜人贷不仅入选了本届大会的“中国Fintech独角兽榜Top50”&#xff0c;推出的YEP共享平台也受到了众多金融机构的关注。从头部平台宜人贷全面开放金融科技能力来看&#xff0c;互联网金融行业历经混…

Redis源码和java jdk源码中hashcode的不同实现

一.redis实际上是使用了siphash 这个比较简单&#xff0c;我说的简单是指redis代码比较少不像jdk一样调用C代码调用栈非常深。 先看这个rehashing.c 主要就是dictKeyHash函数&#xff0c;需要调用dict.h头文件中定义的dictGenHashFunction #include "redis.h" #i…

android 7.0 短信监控,Android 7.0 监听网络变化的示例代码

Android7.0前&#xff0c;Android系统前网络切换时&#xff0c;会发广播&#xff0c;业务只要监听广播即可。public class NetChangeReceiver extends BroadcastReceiver {private static final String ANDROID_NET_CHANGE_ACTION "android.net.conn.CONNECTIVITY_CHANGE…

Mysql列类型-数值型

2019独角兽企业重金招聘Python工程师标准>>> 一、整数型&#xff1a; 1、取值范围&#xff1a; tinyint smallint mediumint int bigint 分别占用1、2、3、4、8个字节的存储空间 如&#xff1a;tinyint占用1个字节空间&#xff0c;它的取值范围&…

2018.10.22-dtoi1443奶牛逃亡(cowrun)

题目描述&#xff1a; Farmer John忘记修复他农场篱笆上的一个大洞&#xff0c;以至于篱笆围着的N&#xff08;1< N <1,000&#xff09;只奶牛从大洞中逃脱出来&#xff0c;并在农场里横冲直撞。每头在篱笆外的奶牛每分钟都将给他带来一美元的损失。FJ必须遍及每头奶牛、…

[转载]Linux 线程实现机制分析

自从多线程编程的概念出现在 Linux 中以来&#xff0c;Linux 多线应用的发展总是与两个问题脱不开干系&#xff1a;兼容性、效率。本文从线程模型入手&#xff0c;通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足&#xff0c;描述了 Linux 社区是如何看待…

Java12和Jdk12安装以及OpenJdk12源码

文档&#xff1a; JDK 12文档:https://docs.oracle.com/en/java/javase/12/ 下载&#xff1a; OracleJDK12下载&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/jdk12-downloads-5295953.html csdn上我下好的&#xff0c;速度较快&#xff1a;https…

android 获取视频大小,Android 获取视频缩略图(获取视频每帧数据)的优化方案

速度对比左边的图片是通过方式1右边的图片是通过方式2speed.gif速度优化&#xff0c;效果拔群。在缩小2倍的Bitmap输出情况下使用MediaMetadataRetriever 抽帧的速度&#xff0c;每帧稳定在 300ms左右。使用MediaCodecImageReader 第一次抽帧。大概是200ms ,后续每帧则是50ms左…

Msql的DML、DDL、DCL的区别

DML(data manipulation language)&#xff1a;它们是SELECT、UPDATE、INSERT、DELETE&#xff0c;这4条命令是用来对数据库里的数据进行操作的语言 DDL(data definition language)&#xff1a;主要的命令有CREATE、ALTER、DROP等&#xff0c;DDL主要是用在定义或改变表(TABLE)的…

JSR 133 Java内存模型以及并发编程的最权威论文汇总

Java内存模型 先看官方文档&#xff1a; https://docs.oracle.com/javase/specs/ JSR 133&#xff1a;Java TM内存模型和线程规范修订版&#xff1a;https://www.jcp.org/en/jsr/detail?id133 JSR&#xff1a;Java规范请求所有JSR的列表&#xff1a;https://jcp.org/en/jsr/…

ajax实现自动刷新页面实例

html部分&#xff1a;<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>ajax实现自动刷新</title> </head> <body onLoad"Autofresh()"> <p>现在的时间是&#xff1a…

android aliasactivity作用,android activity-alias 的作用

activity-alias是android里为了重复使用Activity而设计的。当在Activity的onCreate()方法里&#xff0c;执行getIntent().getComponent().getClassName();得到的可能不是这个Activity的名字&#xff0c;有可能是别名的名字&#xff0c;例如&#xff1a;在AndroidMenifest.xml有…

1024 程序员节 | 请对身边的程序猿好一点

程序员节起源程序员的工作我们都知道&#xff0c;编程嘛。但为什么程序员节要在1024呢&#xff1f;1024最早火起来是因为一个“不可描述”的论坛&#xff0c;那里的回帖机制是&#xff1a;新用户发过贴之后&#xff0c;过1024秒才能发一贴&#xff0c;如果没到1024秒就又发了一…

stackoverflow上一个最会举例子的专家

https://stackoverflow.com/ Premraj是stackoverflow上一个一个最会举例子的专家&#xff0c;我特意收集了他的一些有趣的举例&#xff1a; Java弱引用最精彩的解释 https://stackoverflow.com/questions/299659/whats-the-difference-between-softreference-and-weakrefere…

Java中的两个关键字——super、this

Java中的两个关键字——super、this 神话丿小王子的博客主页 一、super super 是java中方的一个关键字&#xff0c;用它可以引用父类中的成员&#xff1a; super可用于访问父类中定义的属性 super可用于调用父类中定义的成员方法 super可用于在子类构造器中调用父类的构造器 使…

android system window,Android控件的fitSystemWindows属性

官方描述&#xff1a;根据系统窗体里的元素比如状态栏来调整View的布局。如果被设为true&#xff0c;控件的padding将会被调整为顶部留出一个statusBar的空间。类似于伪代码paddingTop"statusBarHeight"。重点说明&#xff1a;当布局内容可以延伸到状态栏&#xff0c…

Nestjs OpenAPI(Swagger)

官方文档 用来描述api 转载于:https://www.cnblogs.com/ajanuw/p/9846589.html

Jdk11,Jdk12的低延迟垃圾收集器ZGC

https://wiki.openjdk.java.net/display/zgc/Main Z垃圾收集器&#xff0c;也称为ZGC&#xff0c;是一种可扩展的低延迟垃圾收集器&#xff0c;旨在实现以下目标&#xff1a; 暂停时间不超过10毫秒暂停时间不会随堆或实时设置大小而增加处理堆范围从几百M到几T字节大小 一目了…

Android项目驱动式开发教程 第2版,《Android项目驱动式开发教程》第一章开发入门.ppt...

《Android项目驱动式开发教程》第一章开发入门1.4 项目框架分析 4 android:versionName"1.0" > 5 8 第9行代码android:icon用来声明整个APP的图标&#xff0c;图片一般都放在drawable文件夹下&#xff0c;使用资源引用的方式。 第10行代码android:label用来声明整…

getLocationInWindow getLocationOnScreen getLeft , getTop, getBottom,getRight

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 最近做项目时&#xff0c;发现在activity的onCreate()和onResume()方法里调用View.getLocationInWindow() 时&#xff0c;View.getLocationInWindow()返回空值&#xff0c;觉得很奇怪&#xff0c;因…