File.deleteOnExit()無法刪除文件

背景:

    項目中發(fā)現(xiàn)文件服務(wù)的臨時文件越來越多導(dǎo)致磁盤吃滿梯找。根據(jù)文件名生成的規(guī)則找到代碼相關(guān)模塊诊胞,乍一看這個代碼貌似沒問題。(可能把Exit看錯exist)
    /**
     * 上傳后清除臨時壓縮包
     *
     * @param zipFilePath
     */
    public void deleteTempFile(String zipFilePath) {
        try {
            File file = new File(zipFilePath);
            if (file.exists()) {
                file.deleteOnExit();
            }
        } catch (Exception e) {
            bizLog.warn("上海銀行臨時文件清理出錯, zipFilePath={}", zipFilePath);
        }
    }
點(diǎn)進(jìn)來源碼一看睦授,我丟···DeleteOnExitHook.add(path); 原來是系統(tǒng)退出時執(zhí)行刪除鉤子两芳,那為什么我們重啟項目沒有將這些臨時文件刪除掉呢?疑問
    /**
     * Requests that the file or directory denoted by this abstract
     * pathname be deleted when the virtual machine terminates.
     * Files (or directories) are deleted in the reverse order that
     * they are registered. Invoking this method to delete a file or
     * directory that is already registered for deletion has no effect.
     * Deletion will be attempted only for normal termination of the
     * virtual machine, as defined by the Java Language Specification.
     *
     * <p> Once deletion has been requested, it is not possible to cancel the
     * request.  This method should therefore be used with care.
     *
     * <P>
     * Note: this method should <i>not</i> be used for file-locking, as
     * the resulting protocol cannot be made to work reliably. The
     * {@link java.nio.channels.FileLock FileLock}
     * facility should be used instead.
     *
     * @throws  SecurityException
     *          If a security manager exists and its <code>{@link
     *          java.lang.SecurityManager#checkDelete}</code> method denies
     *          delete access to the file
     *
     * @see #delete
     *
     * @since 1.2
     */
    public void deleteOnExit() {
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkDelete(path);
        }
        if (isInvalid()) {
            return;
        }
        DeleteOnExitHook.add(path);
    }
繼續(xù)點(diǎn)add方法里面看
/*
 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.io;

import java.util.*;
import java.io.File;

/**
 * This class holds a set of filenames to be deleted on VM exit through a shutdown hook.
 * A set is used both to prevent double-insertion of the same file as well as offer
 * quick removal.
 */

class DeleteOnExitHook {
    private static LinkedHashSet<String> files = new LinkedHashSet<>();
    static {
        // DeleteOnExitHook must be the last shutdown hook to be invoked.
        // Application shutdown hooks may add the first file to the
        // delete on exit list and cause the DeleteOnExitHook to be
        // registered during shutdown in progress. So set the
        // registerShutdownInProgress parameter to true.
        sun.misc.SharedSecrets.getJavaLangAccess()
        //在這里初始化這個類的時候就注冊一個線程任務(wù)執(zhí)行鉤子
            .registerShutdownHook(2 /* Shutdown hook invocation order */,   
                true /* register even if shutdown in progress */,
                new Runnable() {
                    public void run() {
                       runHooks();
                    }
                }
        );
    }

    private DeleteOnExitHook() {}

    static synchronized void add(String file) {
        if(files == null) {
            // DeleteOnExitHook is running. Too late to add a file
            throw new IllegalStateException("Shutdown in progress");
        }
        // 收集要被刪除的文件地址
        files.add(file);
    }
    //執(zhí)行鉤子去枷,  那什么時候執(zhí)行鉤子呢怖辆?
    static void runHooks() {
        LinkedHashSet<String> theFiles;

        synchronized (DeleteOnExitHook.class) {
            theFiles = files;
            files = null;
        }

        ArrayList<String> toBeDeleted = new ArrayList<>(theFiles);

        // reverse the list to maintain previous jdk deletion order.
        // Last in first deleted.
        Collections.reverse(toBeDeleted);
        for (String filename : toBeDeleted) {
            //遍歷所有文件將其刪除
            (new File(filename)).delete();
        }
    }
}
繼續(xù)看下這個方法 registerShutdownHook
   private static void setJavaLangAccess() {
       // Allow privileged classes outside of java.lang
       sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){
           public sun.reflect.ConstantPool getConstantPool(Class<?> klass) {
               return klass.getConstantPool();
           }
           public boolean casAnnotationType(Class<?> klass, AnnotationType oldType, AnnotationType newType) {
               return klass.casAnnotationType(oldType, newType);
           }
           public AnnotationType getAnnotationType(Class<?> klass) {
               return klass.getAnnotationType();
           }
           public Map<Class<? extends Annotation>, Annotation> getDeclaredAnnotationMap(Class<?> klass) {
               return klass.getDeclaredAnnotationMap();
           }
           public byte[] getRawClassAnnotations(Class<?> klass) {
               return klass.getRawAnnotations();
           }
           public byte[] getRawClassTypeAnnotations(Class<?> klass) {
               return klass.getRawTypeAnnotations();
           }
           public byte[] getRawExecutableTypeAnnotations(Executable executable) {
               return Class.getExecutableTypeAnnotationBytes(executable);
           }
           public <E extends Enum<E>>
                   E[] getEnumConstantsShared(Class<E> klass) {
               return klass.getEnumConstantsShared();
           }
           public void blockedOn(Thread t, Interruptible b) {
               t.blockedOn(b);
           }
           public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
               // 注冊關(guān)機(jī)鉤子
                 Shutdown.add(slot, registerShutdownInProgress, hook);
           }
           public int getStackTraceDepth(Throwable t) {
               return t.getStackTraceDepth();
           }
           public StackTraceElement getStackTraceElement(Throwable t, int i) {
               return t.getStackTraceElement(i);
           }
           public String newStringUnsafe(char[] chars) {
               return new String(chars, true);
           }
           public Thread newThreadWithAcc(Runnable target, AccessControlContext acc) {
               return new Thread(target, acc);
           }
           public void invokeFinalize(Object o) throws Throwable {
               o.finalize();
           }
       });
   }
關(guān)機(jī)對象
/*
 * Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang;


/**
 * Package-private utility class containing data structures and logic
 * governing the virtual-machine shutdown sequence.
 *
 * @author   Mark Reinhold
 * @since    1.3
 */

class Shutdown {

    /* Shutdown state */
    private static final int RUNNING = 0;
    private static final int HOOKS = 1;
    private static final int FINALIZERS = 2;
    private static int state = RUNNING;

    /* Should we run all finalizers upon exit? */
    private static boolean runFinalizersOnExit = false;

    // The system shutdown hooks are registered with a predefined slot.
    // The list of shutdown hooks is as follows:
    // (0) Console restore hook
    // (1) Application hooks
    // (2) DeleteOnExit hook
    private static final int MAX_SYSTEM_HOOKS = 10;
    private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];

    // the index of the currently running shutdown hook to the hooks array
    private static int currentRunningHook = 0;

    /* The preceding static fields are protected by this lock */
    private static class Lock { };
    private static Object lock = new Lock();

    /* Lock object for the native halt method */
    private static Object haltLock = new Lock();

    /* Invoked by Runtime.runFinalizersOnExit */
    static void setRunFinalizersOnExit(boolean run) {
        synchronized (lock) {
            runFinalizersOnExit = run;
        }
    }


    /**
     * Add a new shutdown hook.  Checks the shutdown state and the hook itself,
     * but does not do any security checks.
     *
     * The registerShutdownInProgress parameter should be false except
     * registering the DeleteOnExitHook since the first file may
     * be added to the delete on exit list by the application shutdown
     * hooks.
     *
     * @params slot  the slot in the shutdown hook array, whose element
     *               will be invoked in order during shutdown
     * @params registerShutdownInProgress true to allow the hook
     *               to be registered even if the shutdown is in progress.
     * @params hook  the hook to be registered
     *
     * @throw IllegalStateException
     *        if registerShutdownInProgress is false and shutdown is in progress; or
     *        if registerShutdownInProgress is true and the shutdown process
     *           already passes the given slot
     */
    static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
        synchronized (lock) {
            if (hooks[slot] != null)
                throw new InternalError("Shutdown hook at slot " + slot + " already registered");

            if (!registerShutdownInProgress) {
                if (state > RUNNING)
                    throw new IllegalStateException("Shutdown in progress");
            } else {
                if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
                    throw new IllegalStateException("Shutdown in progress");
            }
           // 注冊鉤子線程任務(wù)
            hooks[slot] = hook;
        }
    }

    /* Run all registered shutdown hooks
    *  當(dāng)系統(tǒng)關(guān)機(jī)時遍歷運(yùn)行所有鉤子線程任務(wù),删顶,被調(diào)用方都在同類方法里 竖螃,這里就重復(fù)粘貼
     */
    private static void runHooks() {
        for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
            try {
                Runnable hook;
                synchronized (lock) {
                    // acquire the lock to make sure the hook registered during
                    // shutdown is visible here.
                    currentRunningHook = i;
                    hook = hooks[i];
                }
                if (hook != null) hook.run();
            } catch(Throwable t) {
                if (t instanceof ThreadDeath) {
                    ThreadDeath td = (ThreadDeath)t;
                    throw td;
                }
            }
        }
    }

    /* The halt method is synchronized on the halt lock
     * to avoid corruption of the delete-on-shutdown file list.
     * It invokes the true native halt method.
     */
    static void halt(int status) {
        synchronized (haltLock) {
            halt0(status);
        }
    }

    static native void halt0(int status);

    /* Wormhole for invoking java.lang.ref.Finalizer.runAllFinalizers */
    private static native void runAllFinalizers();


    /* The actual shutdown sequence is defined here.
     *
     * If it weren't for runFinalizersOnExit, this would be simple -- we'd just
     * run the hooks and then halt.  Instead we need to keep track of whether
     * we're running hooks or finalizers.  In the latter case a finalizer could
     * invoke exit(1) to cause immediate termination, while in the former case
     * any further invocations of exit(n), for any n, simply stall.  Note that
     * if on-exit finalizers are enabled they're run iff the shutdown is
     * initiated by an exit(0); they're never run on exit(n) for n != 0 or in
     * response to SIGINT, SIGTERM, etc.
     */
    private static void sequence() {
        synchronized (lock) {
            /* Guard against the possibility of a daemon thread invoking exit
             * after DestroyJavaVM initiates the shutdown sequence
             */
            if (state != HOOKS) return;
        }
        runHooks();
        boolean rfoe;
        synchronized (lock) {
            state = FINALIZERS;
            rfoe = runFinalizersOnExit;
        }
        if (rfoe) runAllFinalizers();
    }


    /* Invoked by Runtime.exit, which does all the security checks.
     * Also invoked by handlers for system-provided termination events,
     * which should pass a nonzero status code.
     */
    static void exit(int status) {
        boolean runMoreFinalizers = false;
        synchronized (lock) {
            if (status != 0) runFinalizersOnExit = false;
            switch (state) {
            case RUNNING:       /* Initiate shutdown */
                state = HOOKS;
                break;
            case HOOKS:         /* Stall and halt */
                break;
            case FINALIZERS:
                if (status != 0) {
                    /* Halt immediately on nonzero status */
                    halt(status);
                } else {
                    /* Compatibility with old behavior:
                     * Run more finalizers and then halt
                     */
                    runMoreFinalizers = runFinalizersOnExit;
                }
                break;
            }
        }
        if (runMoreFinalizers) {
            runAllFinalizers();
            halt(status);
        }
        synchronized (Shutdown.class) {
            /* Synchronize on the class object, causing any other thread
             * that attempts to initiate shutdown to stall indefinitely
             */
            sequence();
            halt(status);
        }
    }


    /* Invoked by the JNI DestroyJavaVM procedure when the last non-daemon
     * thread has finished.  Unlike the exit method, this method does not
     * actually halt the VM.
     */
    static void shutdown() {
        synchronized (lock) {
            switch (state) {
            case RUNNING:       /* Initiate shutdown */
                state = HOOKS;
                break;
            case HOOKS:         /* Stall and then return */
            case FINALIZERS:
                break;
            }
        }
        synchronized (Shutdown.class) {
            sequence();
        }
    }

}
那么問題來了,為什么重啟應(yīng)用時為什么不執(zhí)行鉤子逗余?

原因:

我們項目重啟的腳本先執(zhí)行kill 進(jìn)程的命令 再啟動進(jìn)程特咆,我們都知道kill -9 是強(qiáng)制殺掉進(jìn)程,導(dǎo)致進(jìn)程還沒執(zhí)行關(guān)機(jī)鉤子就被殺掉了進(jìn)程猎荠。屬于暴力殺程序坚弱。所以同事為了方便腳本中都是使用 -9 。但是一般建議是使用kill -15 通知進(jìn)程關(guān)機(jī)命令关摇,讓進(jìn)程執(zhí)行完存在的業(yè)務(wù)邏輯以及執(zhí)行關(guān)機(jī)鉤子相關(guān)業(yè)務(wù)再殺掉進(jìn)程荒叶,這樣不影響自身的業(yè)務(wù)流。
kill相關(guān)命令有大神總結(jié)的更好就引用了一下
linux kill命令參數(shù)及用法詳解 - etwits - 博客園 (cnblogs.com)

驗證:

再springboot 啟動類中添加如下代碼

/**
 * 服務(wù)啟動類(腳手架)
 *
 * @author open-gatway服務(wù)
 * @date 2021-09-13 15:17:25
 **/
@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableFeignClients(basePackages = "com.jdh")
public class OpenGatewayApplication {

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                //添加個日志輸出的鉤子
                System.out.println("我胡漢三又回來了");
            }
        });
        SpringApplication.run(OpenGatewayApplication.class, args);

    }
}
 Linux上面把jar 跑起來

1输虱、執(zhí)行kill -15 看是否復(fù)核預(yù)期些楣,嗯···正常輸出胡漢三又回來的日志;


kill-15回調(diào)鉤子.png

2、再驗證一下kill -9,,麻也沒有愁茁,進(jìn)程直接被干掉了蚕钦;


kill-9強(qiáng)制.png

總結(jié)

要么使用File.delete(); 直接刪除文件。要么使用File.deleteOnExit() 重啟命令用kill -15 ;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹅很,一起剝皮案震驚了整個濱河市嘶居,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌促煮,老刑警劉巖邮屁,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異菠齿,居然都是意外死亡佑吝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門绳匀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芋忿,“玉大人,你說我怎么就攤上這事疾棵「旮郑” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵陋桂,是天一觀的道長逆趣。 經(jīng)常有香客問我,道長嗜历,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任抖所,我火速辦了婚禮梨州,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘田轧。我一直安慰自己暴匠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布傻粘。 她就那樣靜靜地躺著每窖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪弦悉。 梳的紋絲不亂的頭發(fā)上窒典,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機(jī)與錄音稽莉,去河邊找鬼瀑志。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的劈猪。 我是一名探鬼主播昧甘,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼战得!你這毒婦竟也來了充边?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤常侦,失蹤者是張志新(化名)和其女友劉穎痛黎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刮吧,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湖饱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了杀捻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片井厌。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖致讥,靈堂內(nèi)的尸體忽然破棺而出仅仆,到底是詐尸還是另有隱情,我是刑警寧澤垢袱,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布墓拜,位于F島的核電站,受9級特大地震影響请契,放射性物質(zhì)發(fā)生泄漏咳榜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一爽锥、第九天 我趴在偏房一處隱蔽的房頂上張望涌韩。 院中可真熱鬧,春花似錦氯夷、人聲如沸臣樱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雇毫。三九已至,卻和暖如春踩蔚,著一層夾襖步出監(jiān)牢的瞬間棚放,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工寂纪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留席吴,地道東北人赌结。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像孝冒,于是被迫代替她去往敵國和親柬姚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1 多益網(wǎng)絡(luò)面試 Q:博客項目里面如何驗證賬號密碼的庄涡?有沒有做什么安全措施 A: 在登錄表單中填寫用戶名和密碼后量承,...
    全村希望gone閱讀 894評論 0 3
  • Linux習(xí)慣問題: 在vim編輯時,按了ctrl + s后穴店,再按ctrl + q就可以繼續(xù)執(zhí)行了撕捍。ctrl + ...
    光著腳的鞋閱讀 4,517評論 0 16
  • df命令是linux系統(tǒng)以磁盤分區(qū)為單位查看文件系統(tǒng)忧风,可以加上參數(shù)查看磁盤剩余空間信息,命令格式:df -hl顯示...
    果冉多閱讀 323評論 0 0
  • 文件結(jié)構(gòu) /:根目錄球凰,所有的目錄狮腿、文件、設(shè)備都在/之下呕诉,/就是Linux文件系統(tǒng)的組織者缘厢,也是最上級的領(lǐng)導(dǎo)者。 /...
    郝赫閱讀 295評論 0 0
  • 今天遇到一個已刪除文件占用磁盤的情況甩挫,這里記錄簡單的分析和處理流程 背景:1贴硫、接收到磁盤告警后,用df -h查看伊者,...
    blue_smile閱讀 453評論 0 0