新乐企业网站建设,计算机网站建设与管理是什么意思,nat123做视频网站,大连仟亿科技有限公司项目标题与描述
Async-profiler 是一个针对Java的低开销采样性能分析器#xff0c;它克服了传统分析器的“安全点偏差”#xff08;Safepoint bias#xff09;问题。项目利用了HotSpot JVM特有的API来收集堆栈踪迹和跟踪内存分配#xff0c;能够分析非Java线程#xff08…项目标题与描述Async-profiler是一个针对Java的低开销采样性能分析器它克服了传统分析器的“安全点偏差”Safepoint bias问题。项目利用了HotSpot JVM特有的API来收集堆栈踪迹和跟踪内存分配能够分析非Java线程例如GC和JIT编译线程并在堆栈跟踪中显示本地和内核帧。它支持多种分析模式包括CPU时间、Java堆内存分配、本地内存分配与泄漏、竞争锁、硬件和软件性能计数器如缓存未命中、页面错误、上下文切换等。功能特性无安全点偏差采样采用异步采样机制避免传统Java分析器的固有缺陷。多维度性能分析CPU时间分析Java堆内存分配分析本地内存分配与泄漏跟踪锁竞争分析硬件/软件性能计数器分析如缓存未命中、页面错误等线程与栈帧分析监控非Java线程如GC、JIT线程在堆栈跟踪中展示本地Native和内核Kernel帧丰富的输出格式交互式火焰图Flame GraphJava飞行记录器JFR格式OpenTelemetry格式文本格式报告灵活的集成方式命令行工具asprofJava API原生APIC API可通过代理方式集成平台支持官方支持Linuxx64, arm64和macOSx64, arm64平台社区支持其他架构端口如x86, arm32, ppc64le, riscv64, loongarch64多种采样引擎Perf EventsLinux时钟定时器CTimer, ITimerJava方法追踪与延迟分析Instrumentation安装指南下载稳定版本可直接从GitHub Releases页面下载最新稳定版本如v4.2.1的预编译二进制包Linux x64:async-profiler-4.2.1-linux-x64.tar.gzLinux arm64:async-profiler-4.2.1-linux-arm64.tar.gzmacOS arm64/x64:async-profiler-4.2.1-macos.zip从源码构建最低要求GNU MakeGCC 7.5.0 或 Clang 7.0.0静态版libstdc例如在Amazon Linux 2023上yum install libstdc-staticJDK 11构建步骤确保gcc、g和java在PATH环境变量中。导航到async-profiler源码根目录。运行make命令。构建完成后启动器asprof将位于build/bin/asprof目录下。可选运行make test进行单元和集成测试或运行make release打包二进制文件。使用说明快速开始分析一个正在运行的Java应用通常只需使用asprof命令并指定Java进程的PID。# 分析PID为1234的进程持续30秒并将结果保存为交互式火焰图$ asprof -d30-f flamegraph.html1234基础使用示例CPU性能分析$ asprof -e cpu -d60-o cpu_profile.htmlPID内存分配分析每分配512KB采样一次$ asprof -e alloc -d30-f alloc_flame.htmlPID锁竞争分析$ asprof -e lock -d30-f lock_profile.htmlPID输出为JFR格式$ asprof -d30-o profile.jfrPIDJava API集成Async-profiler提供了Java API可以直接在Java代码中调用。importone.profiler.AsyncProfiler;publicclassProfilerDemo{publicstaticvoidmain(String[]args)throwsException{AsyncProfilerprofilerAsyncProfiler.getInstance();// 启动CPU分析profiler.start(cpu,10000000);// 10ms间隔Thread.sleep(30000);// 运行30秒// 停止并保存结果Stringoutputprofiler.stop();System.out.println(output);}}原生API (C API) 集成对于非Java应用或需要更精细控制的场景可以使用原生C API。#includeasprof.hintmain(){asprof_init();asprof_error_terrasprof_execute(start,eventcpu,interval10ms,NULL);if(err!NULL){fprintf(stderr,Profiler error: %s\n,asprof_error_str(err));}// ... 运行被分析的代码 ...errasprof_execute(stop,fileprofile.jfr,NULL);return0;}核心代码1. CPU采样引擎信号处理核心逻辑 (cpuEngine.cpp)此代码段展示了CPU采样引擎如何处理采样信号记录执行样本。/* * Copyright The async-profiler authors * SPDX-License-Identifier: Apache-2.0 */#includecpuEngine.h#includeprofiler.h#includetsc.hvoidCpuEngine::signalHandler(intsigno,siginfo_t*siginfo,void*ucontext){if(!_enabled)return;ExecutionEventevent(TSC::ticks());// 当估算总CPU时间时计算错过的样本数u64 total_cpu_time_count_overrun?u64(_interval)*(1OS::overrun(siginfo)):u64(_interval);Profiler::instance()-recordSample(ucontext,total_cpu_time,EXECUTION_SAMPLE,event);}代码注释signalHandler当配置的采样信号如SIGPROF触发时被调用。_enabled静态标志指示分析器是否处于活动状态。ExecutionEvent封装采样时间戳的简单事件对象。TSC::ticks()使用时间戳计数器TSC获取高精度纳秒级时间。OS::overrun(siginfo)在支持的情况下估算因信号队列满而丢失的样本数量用于更准确地计算总CPU时间。Profiler::instance()-recordSample将采样事件包含上下文、时间、事件类型传递给核心分析器进行记录和处理。2. 内存分配跟踪引擎 (allocTracer.cpp)此代码展示了如何通过设置断点来拦截JVM内部的内存分配方法实现堆内存分配的采样。/* * Copyright The async-profiler authors * SPDX-License-Identifier: Apache-2.0 */#includeallocTracer.h#includeprofiler.h#includestackFrame.h#includetsc.h#includevmStructs.h// 当我们的断点陷阱被触发时调用voidAllocTracer::trapHandler(intsigno,siginfo_t*siginfo,void*ucontext){StackFrameframe(ucontext);EventType event_type;uintptr_t total_size;uintptr_t instance_size;// PC指向BREAKPOINT指令或下一条指令if(_in_new_tlab.covers(frame.pc())){// send_allocation_in_new_tlab(...)event_typeALLOC_SAMPLE;total_size_trap_kind1?frame.arg2():frame.arg1();instance_size_trap_kind1?frame.arg3():frame.arg2();}elseif(_outside_tlab.covers(frame.pc())){// send_allocation_outside_tlab(...)event_typeALLOC_OUTSIDE_TLAB;total_size_trap_kind1?frame.arg2():frame.arg1();instance_size0;}else{// 不是我们的陷阱交给其他处理程序Profiler::instance()-trapHandler(signo,siginfo,ucontext);return;}// 通过模拟“ret”指令离开被跟踪的函数uintptr_t klassframe.arg0();frame.ret();if(_enabledupdateCounter(_allocated_bytes,total_size,_interval)){recordAllocation(ucontext,event_type,klass,total_size,instance_size);}}代码注释trapHandler处理由分配断点触发的信号。StackFrame frame(ucontext)从信号上下文(ucontext)中解析出栈帧信息。_in_new_tlab,_outside_tlabTrap对象代表在JVM的AllocTracer::send_allocation_in_new_tlab和send_allocation_outside_tlab方法中设置的断点。covers(frame.pc())检查程序计数器(PC)是否位于特定断点的地址范围内。frame.arg0(),arg1(),arg2(),arg3()根据调用约定因JDK版本_trap_kind而异从栈帧或寄存器中提取函数参数如类指针、分配大小。frame.ret()修改上下文模拟从被拦截函数返回使执行流程继续。updateCounter基于配置的采样间隔(_interval)原子地更新已分配字节计数器并决定是否记录当前分配样本。recordAllocation创建并记录分配事件包含类信息、大小和时间戳。3. 栈帧存储与哈希管理 (callTraceStorage.cpp)这段代码是分析器的核心数据结构负责高效地存储和检索调用栈踪迹。/* * Copyright The async-profiler authors * SPDX-License-Identifier: Apache-2.0 */#includecallTraceStorage.h#includeos.hu64CallTraceStorage::calcHash(intnum_frames,ASGCT_CallFrame*frames){u64 h0;for(inti0;inum_frames;i){// 组合方法ID和行号BCI来生成哈希hh*31(uintptr_t)frames[i].method_id;hh*31frames[i].bci;}returnh;}CallTrace*CallTraceStorage::storeCallTrace(intnum_frames,ASGCT_CallFrame*frames){u64 hashcalcHash(num_frames,frames);CallTrace*tracefindCallTrace(_current_table,hash);if(trace!NULL){returntrace;}// 在分配器中为新调用踪迹分配内存size_t sizesizeof(CallTrace)(num_frames-1)*sizeof(ASGCT_CallFrame);trace(CallTrace*)_allocator.alloc(size);if(traceNULL){// 内存不足返回溢出标识_overflow;return_overflow_trace;}trace-num_framesnum_frames;memcpy(trace-frames,frames,num_frames*sizeof(ASGCT_CallFrame));// 将新踪迹插入哈希表u32 call_trace_id_current_table-incSize();if(call_trace_id_current_table-capacity()){// 哈希表已满分配新的更大容量的表LongHashTable*new_tableLongHashTable::allocate(_current_table,_current_table-capacity()*2);if(new_table!NULL){_current_tablenew_table;call_trace_id0;}else{_overflow;return_overflow_trace;}}u64*keys_current_table-keys();CallTraceSample*values_current_table-values();keys[call_trace_id]hash;values[call_trace_id].setTrace(trace);values[call_trace_id].samples0;values[call_trace_id].counter0;returntrace;}代码注释CallTraceStorage管理所有唯一调用栈踪迹的存储。calcHash根据调用栈中所有帧的方法ID和行号BCI计算一个哈希值用于快速查找。storeCallTrace存储一个新的调用栈踪迹。首先通过findCallTrace在哈希表中查找是否已存在相同栈。如果不存在使用LinearAllocator(_allocator) 分配内存。这是一个高性能的自定义分配器用于快速分配小对象。如果分配失败或哈希表扩容失败递增溢出计数器并返回一个预定义的“溢出”踪迹。LongHashTable一个两级哈希表结构支持并发插入和动态扩容。incSize()原子地增加哈希表大小并返回新条目的索引。setTrace(trace)使用原子存储将踪迹指针设置到哈希表的值槽中确保多线程环境下的内存可见性。该设计实现了去重相同的调用栈只在内存中存储一次后续采样只增加该栈对应的计数器极大节省了内存空间。更多精彩内容 请关注我的个人公众号 公众号办公AI智能小助手对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号网络安全技术点滴分享