最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用VisualVM分析日志

 更新時間:2025年07月17日 09:51:36   作者:小凱 ?  
文章強調(diào)程序員需掌握多種工具(如JMeter、ELK、Prometheus等)應對工作挑戰(zhàn),避免線上事故,重點介紹VisualVM作為Java故障排查工具,通過分析大對象和GC監(jiān)控,提升代碼質(zhì)量與問題定位效率

很多人沒畢業(yè)前以為學編程,以為工作后就只是寫代碼。工作后才發(fā)現(xiàn),寫代碼只是一少部分工作。

JMeter 壓測、Remote JVM Debug - 遠程調(diào)試、AREX - 流量錄制&回放、ELK - 分布式日志、普羅米修斯監(jiān)控、Arthas、Dump日志分析等,但凡一樣不會,基本就會在某一個場景踩坑。小則是報警異常,大則是線上事故!

從互聯(lián)網(wǎng)草蜢時代,到現(xiàn)在工作了這么多年,也是見證了很多程序員因為寫B(tài)ug畢業(yè)啦,即使不是被開除,往往重大的事故也會影響未來的績效、加薪和晉升。這些事故按;照影響時長、影響用戶量、造成的資損、解決的時長等,會被定級為 P0、P1、P2、P3 不同級別的事故。

所以,到目前有越來越多的輔助工具,來幫助研發(fā)提高代碼交付質(zhì)量,以及各類系統(tǒng)異常分析工具,提高問題排查效率。類似這樣的系統(tǒng)、服務、組件,今天給大家分享一個關(guān)于 VisualVM 的使用。

一、關(guān)于 VisualVM

VisualVM 是一款可視化 Java 故障排除工具,集成了 JDK 命令行工具和輕量級性能分析功能。專為開發(fā)和生產(chǎn)環(huán)境設計。

下載:https://visualvm.github.io/download.html(opens new window)

接下來,結(jié)合 VisualVM 做一些常用的案例,方便伙伴學習。

二、案例 - 分析大對象

1. 測試工程

  • 說明:這是一個簡單的測試工程,通過訪問接口產(chǎn)生大對象。之后在通過 JmapDumpController 接口,執(zhí)行命令,產(chǎn)生 Dump 文件。之后在使用 VisualVM 分析產(chǎn)生的 Dump 日志,定位是哪個對象導致的問題。

2. 執(zhí)行程序

首先,啟動應用程序。之后執(zhí)行 visualvm-test.sh 腳本,Windows 用戶需要在 powershell 里執(zhí)行,Mac 電腦可以直接在 IntelliJ IDEA 點擊綠色箭頭執(zhí)行。

  • 首先,點擊啟動程序,本地運行即可。一般公司里線上的應用,也有專門下載 dump 日志的地方。
  • 之后,執(zhí)行 ./visualvm-test.sh 這部分寫了測試程序的腳本和獲取 dump 日志的操作。

2.1 接口;創(chuàng)建對象

@RestController
@RequestMapping("/api/memory")
public class MemoryTestController {

    // 用于存儲大對象的靜態(tài)變量,模擬內(nèi)存泄漏
    private static final Map<String, Object> MEMORY_CACHE = new ConcurrentHashMap<>();
    private static final List<byte[]> BIG_OBJECTS = new ArrayList<>();

    /**
     * 大對象接口 - 創(chuàng)建大量對象占用內(nèi)存
     */
    @GetMapping("/big-object")
    public Map<String, Object> bigObjectApi() {
        // 創(chuàng)建大對象(10MB的字節(jié)數(shù)組)
        byte[] bigData = new byte[10 * 1024 * 1024]; // 10MB
        for (int i = 0; i < bigData.length; i++) {
            bigData[i] = (byte) (i % 256);
        }
        
        // 將大對象存儲到靜態(tài)集合中,模擬內(nèi)存泄漏
        BIG_OBJECTS.add(bigData);
        
        Map<String, Object> result = new HashMap<>();
        result.put("status", "success");
        result.put("message", "創(chuàng)建了一個大對象(10MB)");
        result.put("timestamp", System.currentTimeMillis());
        result.put("bigObjectsCount", BIG_OBJECTS.size());
        result.put("totalMemoryUsed", BIG_OBJECTS.size() * 10 + "MB");
        
        return result;
    }

    /**
     * 內(nèi)存泄漏接口 - 持續(xù)創(chuàng)建對象并緩存
     */
    @GetMapping("/memory-leak")
    public Map<String, Object> memoryLeakApi() {
        String key = "data_" + System.currentTimeMillis();
        
        // 創(chuàng)建大量小對象并緩存
        List<String> dataList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            dataList.add("這是第" + i + "個數(shù)據(jù)對象,包含一些文本內(nèi)容用于占用內(nèi)存空間");
        }
        
        MEMORY_CACHE.put(key, dataList);
        
        Map<String, Object> result = new HashMap<>();
        result.put("status", "success");
        result.put("message", "創(chuàng)建了10000個小對象并緩存");
        result.put("timestamp", System.currentTimeMillis());
        result.put("cacheSize", MEMORY_CACHE.size());
        result.put("cacheKey", key);
        
        return result;
    }

    /**
     * 超大對象接口 - 創(chuàng)建超大對象
     */
    @GetMapping("/huge-object")
    public Map<String, Object> hugeObjectApi() {
        // 創(chuàng)建超大對象(100MB的字節(jié)數(shù)組)
        byte[] hugeData = new byte[100 * 1024 * 1024]; // 100MB
        
        // 填充數(shù)據(jù)
        for (int i = 0; i < hugeData.length; i++) {
            hugeData[i] = (byte) (Math.random() * 256);
        }
        
        BIG_OBJECTS.add(hugeData);
        
        Map<String, Object> result = new HashMap<>();
        result.put("status", "success");
        result.put("message", "創(chuàng)建了一個超大對象(100MB)");
        result.put("timestamp", System.currentTimeMillis());
        result.put("bigObjectsCount", BIG_OBJECTS.size());
        
        return result;
    }

}

2.2 接口;獲取日志(dump)

@RestController
@RequestMapping("/api/jmap")
public class JmapDumpController {

    // 使用相對路徑,基于項目根目錄
    private static final String DUMP_DIR = "docs/dump";

    /**
     * 獲取絕對路徑的dump目錄
     */
    private String getDumpDirectory() {
        // 獲取項目根目錄
        String userDir = System.getProperty("user.dir");
        // 如果當前目錄是xfg-dev-tech-app,則需要回到上級目錄
        if (userDir.endsWith("xfg-dev-tech-app")) {
            userDir = new File(userDir).getParent();
        }
        return userDir + File.separator + DUMP_DIR;
    }

    /**
     * 生成堆轉(zhuǎn)儲文件
     */
    @GetMapping("/dump")
    public Map<String, Object> generateHeapDump() {
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 獲取dump目錄的絕對路徑
            String dumpDir = getDumpDirectory();
            
            // 確保目錄存在
            File dir = new File(dumpDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            
            // 獲取當前進程的PID
            String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
            
            // 生成文件名(包含時間戳)
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
            String timestamp = sdf.format(new Date());
            String fileName = "heap_dump_" + timestamp + ".hprof";
            String filePath = dumpDir + File.separator + fileName;
            
            // 執(zhí)行jmap命令生成堆轉(zhuǎn)儲
            String command = "jmap -dump:format=b,file=" + filePath + " " + pid;
            Process process = Runtime.getRuntime().exec(command);
            int exitCode = process.waitFor();
            
            if (exitCode == 0) {
                result.put("status", "success");
                result.put("message", "堆轉(zhuǎn)儲文件生成成功");
                result.put("filePath", filePath);
                result.put("fileName", fileName);
            } else {
                result.put("status", "error");
                result.put("message", "堆轉(zhuǎn)儲文件生成失敗");
                result.put("exitCode", exitCode);
            }
            
        } catch (IOException | InterruptedException e) {
            result.put("status", "error");
            result.put("message", "生成堆轉(zhuǎn)儲文件時發(fā)生異常: " + e.getMessage());
        }
        
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }

}

2.3 腳本;統(tǒng)一執(zhí)行

#!/bin/bash

# VisualVM 內(nèi)存測試自動化腳本
# 作者: xiaofuge
# 用途: 自動化測試內(nèi)存接口并生成dump文件

# 配置參數(shù)
BASE_URL="http://localhost:8091"
DUMP_DIR="../dump"
LOG_FILE="$DUMP_DIR/test_log_$(date +%Y%m%d_%H%M%S).txt"

# 顏色輸出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 日志函數(shù)
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log_info() {
    echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}

# 檢查應用是否啟動
check_app_status() {
    log_info "檢查應用狀態(tài)..."
    response=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/memory/status")
    if [ "$response" = "200" ]; then
        log_success "應用已啟動,狀態(tài)正常"
        return 0
    else
        log_error "應用未啟動或狀態(tài)異常 (HTTP: $response)"
        return 1
    fi
}

# 等待應用啟動
wait_for_app() {
    log_info "等待應用啟動..."
    for i in {1..30}; do
        if check_app_status > /dev/null 2>&1; then
            log_success "應用啟動成功"
            return 0
        fi
        log_info "等待中... ($i/30)"
        sleep 2
    done
    log_error "應用啟動超時"
    return 1
}

# 調(diào)用API接口
call_api() {
    local endpoint=$1
    local description=$2
    local count=${3:-1}
    
    log_info "調(diào)用接口: $description"
    for ((i=1; i<=count; i++)); do
        response=$(curl -s "$BASE_URL$endpoint")
        status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
        if [ "$status" = "success" ]; then
            log_success "[$i/$count] $description - 成功"
        else
            log_error "[$i/$count] $description - 失敗: $response"
        fi
        sleep 1
    done
}

# 顯示內(nèi)存狀態(tài)
show_memory_status() {
    log_info "獲取內(nèi)存狀態(tài)..."
    response=$(curl -s "$BASE_URL/api/memory/status")
    echo "$response" | python3 -m json.tool 2>/dev/null || echo "$response"
    echo ""
}

# 生成dump文件
generate_dump() {
    log_info "生成堆轉(zhuǎn)儲文件..."
    response=$(curl -s "$BASE_URL/api/jmap/dump")
    status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
    if [ "$status" = "success" ]; then
        filename=$(echo "$response" | grep -o '"fileName":"[^"]*"' | cut -d'"' -f4)
        log_success "堆轉(zhuǎn)儲文件生成成功: $filename"
    else
        log_error "堆轉(zhuǎn)儲文件生成失敗: $response"
    fi
}

# 生成內(nèi)存信息文件
generate_memory_info() {
    log_info "生成內(nèi)存信息文件..."
    response=$(curl -s "$BASE_URL/api/jmap/memory-info")
    status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
    if [ "$status" = "success" ]; then
        filename=$(echo "$response" | grep -o '"fileName":"[^"]*"' | cut -d'"' -f4)
        log_success "內(nèi)存信息文件生成成功: $filename"
    else
        log_error "內(nèi)存信息文件生成失敗: $response"
    fi
}

# 清理緩存
clear_cache() {
    log_info "清理緩存..."
    response=$(curl -s "$BASE_URL/api/memory/clear-cache")
    status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
    if [ "$status" = "success" ]; then
        log_success "緩存清理成功"
    else
        log_error "緩存清理失敗: $response"
    fi
}

# 主測試流程
run_test() {
    log_info "開始VisualVM內(nèi)存測試"
    
    # 檢查dump目錄
    if [ ! -d "$DUMP_DIR" ]; then
        log_info "創(chuàng)建dump目錄: $DUMP_DIR"
        mkdir -p "$DUMP_DIR"
    fi
    
    # 等待應用啟動
    if ! wait_for_app; then
        log_error "應用啟動失敗,退出測試"
        exit 1
    fi
    
    # 顯示初始內(nèi)存狀態(tài)
    log_info "=== 初始內(nèi)存狀態(tài) ==="
    show_memory_status
    
    # 測試普通接口
    call_api "/api/memory/normal" "普通接口測試" 5
    
    # 顯示內(nèi)存狀態(tài)
    log_info "=== 普通接口調(diào)用后內(nèi)存狀態(tài) ==="
    show_memory_status
    
    # 測試大對象接口
    call_api "/api/memory/big-object" "大對象接口測試" 10
    
    # 顯示內(nèi)存狀態(tài)
    log_info "=== 大對象創(chuàng)建后內(nèi)存狀態(tài) ==="
    show_memory_status
    
    # 生成第一次dump
    generate_dump
    generate_memory_info
    
    # 測試內(nèi)存泄漏接口
    call_api "/api/memory/memory-leak" "內(nèi)存泄漏接口測試" 20
    
    # 顯示內(nèi)存狀態(tài)
    log_info "=== 內(nèi)存泄漏測試后內(nèi)存狀態(tài) ==="
    show_memory_status
    
    # 測試超大對象接口
    call_api "/api/memory/huge-object" "超大對象接口測試" 5
    
    # 顯示內(nèi)存狀態(tài)
    log_info "=== 超大對象創(chuàng)建后內(nèi)存狀態(tài) ==="
    show_memory_status
    
    # 生成第二次dump
    generate_dump
    generate_memory_info
    
    # 清理緩存
    clear_cache
    
    # 顯示清理后內(nèi)存狀態(tài)
    log_info "=== 緩存清理后內(nèi)存狀態(tài) ==="
    show_memory_status
    
    # 生成第三次dump
    generate_dump
    generate_memory_info
    
    log_success "VisualVM內(nèi)存測試完成"
    log_info "日志文件: $LOG_FILE"
    log_info "dump文件目錄: $DUMP_DIR"
}

# 顯示幫助信息
show_help() {
    echo "VisualVM 內(nèi)存測試腳本"
    echo ""
    echo "用法: $0 [選項]"
    echo ""
    echo "選項:"
    echo "  test          運行完整測試流程"
    echo "  check         檢查應用狀態(tài)"
    echo "  status        顯示內(nèi)存狀態(tài)"
    echo "  dump          生成堆轉(zhuǎn)儲文件"
    echo "  memory-info   生成內(nèi)存信息文件"
    echo "  clear         清理緩存"
    echo "  help          顯示幫助信息"
    echo ""
    echo "示例:"
    echo "  $0 test       # 運行完整測試"
    echo "  $0 check      # 檢查應用狀態(tài)"
    echo "  $0 dump       # 生成dump文件"
}

# 主程序
case "${1:-test}" in
    "test")
        run_test
        ;;
    "check")
        check_app_status
        ;;
    "status")
        show_memory_status
        ;;
    "dump")
        generate_dump
        ;;
    "memory-info")
        generate_memory_info
        ;;
    "clear")
        clear_cache
        ;;
    "help")
        show_help
        ;;
    *)
        log_error "未知選項: $1"
        show_help
        exit 1
        ;;
esac
  • 整個腳本,會幫助我們執(zhí)行接口請求以及獲取 dump 日志。

3. Dump 分析

  • 首先,先通過 VisualVM load dump 日志文件,之后點擊 Instances By Size 大的文件。
  • 之后,對大的文件對象,點擊 references 這樣就可以看到是哪個對象影響的問題了。很快的就能幫你分析出程序內(nèi)產(chǎn)生大的對象的問題原因。

三、案例;GC 插件

VisualVM 還有類似于普羅米修斯 (opens new window)一樣的監(jiān)控,可以查看到 JVM 運行情況。也可以幫助我們分析程序運行情況。

1. 安裝插件 - VisualVM

  • 在 VisualVM 安裝 Visual GC 插件。

2. 安裝插件 - IntelliJ IDEA

  • 也可以給 Intellij IDEA 安裝一個 VisualVM Launcher 插件,啟動程序可以直接使用。

3. 進入監(jiān)控

  • 打開 VisualVM 看到本地啟動的程序,之后打開 Visual GC
  • 這里還可以看見 Monitor、Threads、Profiler,方便我們分析程序

4. GC 說明

  • 如圖,各個模塊展示了 JVM 運行狀況,從這里可以看到程序占用內(nèi)存的情況。如果是壓測驗證,可以打開輔助分析。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java元注解meta-annotation和依賴注入詳解

    Java元注解meta-annotation和依賴注入詳解

    這篇文章主要給大家介紹了關(guān)于Java元注解meta-annotation和依賴注入的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • springboot配置flyway(入門級別教程)

    springboot配置flyway(入門級別教程)

    本文介紹了springboot配置flyway,主要介紹基于SpringBoot集成flyway來管理數(shù)據(jù)庫的變更,具有一定的參考價值,感興趣的可以了解一下
    2023-09-09
  • java實現(xiàn)點擊按鈕彈出新窗體功能

    java實現(xiàn)點擊按鈕彈出新窗體功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)點擊按鈕彈出新窗體功能,舊窗體不進行操作,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • java實現(xiàn)附件預覽(openoffice+swftools+flexpaper)實例

    java實現(xiàn)附件預覽(openoffice+swftools+flexpaper)實例

    本篇文章主要介紹了java實現(xiàn)附件預覽(openoffice+swftools+flexpaper)實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2016-10-10
  • SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例

    SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例

    本文主要介紹了SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04
  • 如何通過Java添加水印到Word文檔

    如何通過Java添加水印到Word文檔

    這篇文章主要介紹了如何通過Java添加水印到Word文檔,水印是一種常用于各種文檔的聲明、防偽手段,一般可設置文字水印或者加載圖片作為水印。以下內(nèi)容將分享通過Java編程給Word文檔添加水印效果的方法,需要的朋友可以參考下
    2019-07-07
  • 一個簡單的Python名片管理系統(tǒng)

    一個簡單的Python名片管理系統(tǒng)

    這篇文章主要為大家詳細介紹了一個簡單的Python名片管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 詳解java CountDownLatch和CyclicBarrier在內(nèi)部實現(xiàn)和場景上的區(qū)別

    詳解java CountDownLatch和CyclicBarrier在內(nèi)部實現(xiàn)和場景上的區(qū)別

    這篇文章主要介紹了詳解java CountDownLatch和CyclicBarrier在內(nèi)部實現(xiàn)和場景上的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • jQuery 動畫效果代碼分享

    jQuery 動畫效果代碼分享

    本文給大家分享一段關(guān)于jquery實現(xiàn)的動畫效果,代碼簡單易懂,非常不錯,感興趣的朋友參考下
    2016-11-11
  • Java版畫板的實現(xiàn)方法

    Java版畫板的實現(xiàn)方法

    這篇文章主要為大家詳細介紹了Java版畫板的實現(xiàn)方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07

最新評論

乐安县| 东乌| 新巴尔虎左旗| 通州市| 牙克石市| 夏津县| 饶阳县| 北辰区| 武平县| 湘阴县| 十堰市| 白山市| 固安县| 越西县| 松原市| 马龙县| 巢湖市| 礼泉县| 郑州市| 镇远县| 湖北省| 玛曲县| 海安县| 温宿县| 雅安市| 手游| 庄河市| 疏附县| 鄂伦春自治旗| 镇康县| 大同县| 平遥县| 安丘市| 罗甸县| 桃园县| 桓台县| 雷州市| 宁晋县| 太原市| 旬阳县| 琼海市|