Подтвердить что ты не робот

Как найти, какой поток Java/Scala заблокировал файл?

Вкратце:

  • Как найти, какой поток Java/Scala заблокировал файл? Я знаю, что класс/поток в JVM заблокировал конкретный файл (перекрывает область файла), но я не знаю, как это сделать. Можно ли узнать, что делает класс/поток, когда я останавливаю приложение в точке останова?

Следующий код выдает OverlappingFileLockException:

FileChannel.open(Paths.get("thisfile"), StandardOpenOption.APPEND).tryLock().isValid();
FileChannel.open(Paths.get("thisfile"), StandardOpenOption.APPEND).tryLock()..isShared();
  1. Как Java/Scala заблокировать этот файл (Spark)? Я знаю, как блокировать файлы с помощью java.nio.channels, но я не нашел соответствующего вызова в репозитории github Spark.

Подробнее о моей проблеме: 1. Когда я запускаю Spark в ОС Windows с помощью Hive, он работает правильно, однако каждый раз, когда Spark отключается, он не может удалить один временный каталог (другой временный каталог до его правильного удаления) и выводит следующее исключение:

2015-12-11 15:04:36 [Thread-13] INFO  org.apache.spark.SparkContext - Successfully stopped SparkContext
2015-12-11 15:04:36 [Thread-13] INFO  o.a.spark.util.ShutdownHookManager - Shutdown hook called
2015-12-11 15:04:36 [Thread-13] INFO  o.a.spark.util.ShutdownHookManager - Deleting directory C:\Users\MyUser\AppData\Local\Temp\spark-9d564520-5370-4834-9946-ac5af3954032
2015-12-11 15:04:36 [Thread-13] INFO  o.a.spark.util.ShutdownHookManager - Deleting directory C:\Users\MyUser\AppData\Local\Temp\spark-42b70530-30d2-41dc-aff5-8d01aba38041
2015-12-11 15:04:36 [Thread-13] ERROR o.a.spark.util.ShutdownHookManager - Exception while deleting Spark temp dir: C:\Users\MyUser\AppData\Local\Temp\spark-42b70530-30d2-41dc-aff5-8d01aba38041
java.io.IOException: Failed to delete: C:\Users\MyUser\AppData\Local\Temp\spark-42b70530-30d2-41dc-aff5-8d01aba38041
    at org.apache.spark.util.Utils$.deleteRecursively(Utils.scala:884) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.ShutdownHookManager$$anonfun$1$$anonfun$apply$mcV$sp$3.apply(ShutdownHookManager.scala:63) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.ShutdownHookManager$$anonfun$1$$anonfun$apply$mcV$sp$3.apply(ShutdownHookManager.scala:60) [spark-core_2.11-1.5.0.jar:1.5.0]
    at scala.collection.mutable.HashSet.foreach(HashSet.scala:78) [scala-library-2.11.6.jar:na]
    at org.apache.spark.util.ShutdownHookManager$$anonfun$1.apply$mcV$sp(ShutdownHookManager.scala:60) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHook.run(ShutdownHookManager.scala:264) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHookManager$$anonfun$runAll$1$$anonfun$apply$mcV$sp$1.apply$mcV$sp(ShutdownHookManager.scala:234) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHookManager$$anonfun$runAll$1$$anonfun$apply$mcV$sp$1.apply(ShutdownHookManager.scala:234) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHookManager$$anonfun$runAll$1$$anonfun$apply$mcV$sp$1.apply(ShutdownHookManager.scala:234) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.Utils$.logUncaughtExceptions(Utils.scala:1699) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHookManager$$anonfun$runAll$1.apply$mcV$sp(ShutdownHookManager.scala:234) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHookManager$$anonfun$runAll$1.apply(ShutdownHookManager.scala:234) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHookManager$$anonfun$runAll$1.apply(ShutdownHookManager.scala:234) [spark-core_2.11-1.5.0.jar:1.5.0]
    at scala.util.Try$.apply(Try.scala:191) [scala-library-2.11.6.jar:na]
    at org.apache.spark.util.SparkShutdownHookManager.runAll(ShutdownHookManager.scala:234) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.spark.util.SparkShutdownHookManager$$anon$2.run(ShutdownHookManager.scala:216) [spark-core_2.11-1.5.0.jar:1.5.0]
    at org.apache.hadoop.util.ShutdownHookManager$1.run(ShutdownHookManager.java:54) [hadoop-common-2.4.1.jar:na]

Я пытаюсь выполнить поиск в Интернете, но обнаружил только проблемы в Spark (один пользователь пытается сделать какой-то патч, но он не работает, если я не выдержу комментарий для этого запроса на загрузку правильно) и некоторые неотвеченные вопросы в SO.

Похоже, проблема заключается в методе deleteRecursively() класса Utils.scala. Я установил точку останова для этого метода и переписал его на Java:

public class Test {
    public static void deleteRecursively(File file) {
        if (file != null) {
            try {
                if (file.isDirectory()) {
                    for (File child : listFilesSafely(file)) {
                        deleteRecursively(child);
                    }
                    //ShutdownHookManager.removeShutdownDeleteDir(file)
                }
            } finally {
                if (!file.delete()) {
                    if (file.exists()) {
                        throw new RuntimeException("Failed to delete: " + file.getAbsolutePath());
                    }
                }
            }
        }
    }

    private static List<File> listFilesSafely(File file) {
        if (file.exists()) {
            File[] files = file.listFiles();
            if (files == null) {
                throw new RuntimeException("Failed to list files for dir: " + file);
            }
            return Arrays.asList(files);
        } else {
            return Collections.emptyList();
        }
    }

    public static void main(String [] arg) {
        deleteRecursively(new File("C:\\Users\\MyUser\\AppData\\Local\\Temp\\spark-9ba0bb0c-1e20-455d-bc1f-86c696661ba3")); 
    }

Когда Spark останавливается в точке останова этого метода, я обнаружил, что JVM одного потока Spark заблокирован "C:\Users\MyUser\AppData\Local\Temp\spark-9ba0bb0c-1e20-455d-bc1f-86c696661ba3\metastore\db.lck" и Windows Process Explorer также показывает, что Java блокирует этот файл. Также FileChannel показывает, что файл заблокирован в JVM.

Теперь я должен:

  • Узнайте, какой поток/класс заблокировал этот файл

  • Узнайте, какой способ блокировки файлов Spark используется для блокировки метастара \db.lck, какой класс делает это и как разблокировать перед завершением работы

  • Сделайте запрос на Spark или Hive, чтобы разблокировать этот файл ( "metastore\db.lck" ) перед вызовом метода deleteRecursively() или, по крайней мере, оставить комментарий о проблеме

Если вам нужна какая-либо другая информация, пожалуйста, спросите в комментариях.

4b9b3361

Ответ 1

  • Как найти, какой поток Java/ Scala заблокировал файл?

У меня есть проблема и выясните это решение: все заблокированные объекты вы можете увидеть, по крайней мере, в поле Thread.threadLocals.

Если файл заблокирован следующим кодом:

    File newFile = new File("newFile.lock");
    newFile.createNewFile();
    FileLock fileLock = FileChannel.open(Paths.get(newFile.getAbsolutePath()), StandardOpenOption.APPEND).tryLock();

В Thread.threadLocals вы можете увидеть класс sun.nio.fs.NativeBuffer с полем owner= ".../newFile.lock".

Итак, вы можете попробовать следующий код, который возвращает весь поток со всем классом в threadLocals, вам нужно найти, какие темы имеют классы NativeBuffer или объекты Spark/Hive и т.д. (и после проверки этого threadLocals этого потока в отладке Eclipse или IDEA mode):

private static String getThreadsLockFile() {
    Set<Thread> threads = Thread.getAllStackTraces().keySet();
    StringBuilder builder = new StringBuilder();
    for (Thread thread : threads) {
        builder.append(getThreadsLockFile(thread));
    }
    return builder.toString();
}

private static String getThreadsLockFile(Thread thread) {
    StringBuffer stringBuffer = new StringBuffer();
    try {
        Field field = thread.getClass().getDeclaredField("threadLocals");
        field.setAccessible(true);
        Object map = field.get(thread);
        Field table = Class.forName("java.lang.ThreadLocal$ThreadLocalMap").getDeclaredField("table");
        table.setAccessible(true);
        Object tbl = table.get(map);
        int length = Array.getLength(tbl);
        for (int i = 0; i < length; i++) {
            try {
                Object entry = Array.get(tbl, i);
                if (entry != null) {
                    Field valueField = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry").getDeclaredField("value");
                    valueField.setAccessible(true);
                    Object value = valueField.get(entry);
                    if (value != null) {
                        stringBuffer.append(thread.getName()).append(" : ").append(value.getClass()).
                                append(" ").append(value).append("\n");
                       }
                }
            } catch (Exception exp) {
                // skip, do nothing
            }
        }
    } catch (Exception exp) {
        // skip, do nothing
    }
    return stringBuffer.toString();
}

Или вы можете попробовать использовать следующий код, но этот код найдет только NativeBuffer класс с параметром owner (так что он не работает во всех случаях):

private static String getThreadsLockFile(String fileName) {
    Set<Thread> threads = Thread.getAllStackTraces().keySet();
    StringBuilder builder = new StringBuilder();
    for (Thread thread : threads) {
        builder.append(getThreadsLockFile(thread, fileName));
    }
    return builder.toString();
}

private static String getThreadsLockFile(Thread thread, String fileName) {
    StringBuffer stringBuffer = new StringBuffer();
    try {
        Field field = thread.getClass().getDeclaredField("threadLocals");
        field.setAccessible(true);
        Object map = field.get(thread);
        Field table = Class.forName("java.lang.ThreadLocal$ThreadLocalMap").getDeclaredField("table");
        table.setAccessible(true);
        Object tbl = table.get(map);
        int length = Array.getLength(tbl);
        for (int i = 0; i < length; i++) {
            try {
                Object entry = Array.get(tbl, i);
                if (entry != null) {
                    Field valueField = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry").getDeclaredField("value");
                    valueField.setAccessible(true);
                    Object value = valueField.get(entry);
                    if (value != null) {
                        int length1 = Array.getLength(value);
                        for (int j = 0; j < length1; j++) {
                            try {
                                Object entry1 = Array.get(value, j);
                                Field ownerField = Class.forName("sun.nio.fs.NativeBuffer").getDeclaredField("owner");
                                ownerField.setAccessible(true);
                                String owner = ownerField.get(entry1).toString();
                                if (owner.contains(fileName)) {
                                    stringBuffer.append(thread.getName());
                                }
                            } catch (Exception exp) {
                                // skip, do nothing
                            }
                        }
                    }
                }
            } catch (Exception exp) {
                // skip, do nothing
            }
        }
    } catch (Exception exp) {
        // skip, do nothing
    }
    return stringBuffer.toString();
}

Ответ 2

Смотрите Как узнать, какой поток блокирует файл в java?

Файлы заблокированы процессом Windows. Темы могут открывать файлы для чтения, но класс, содержащий ссылку на дескриптор файла, отвечает за его закрытие. Поэтому вы должны искать объект, а не поток.

См. Как узнать, что держится на разблокированных объектах?, чтобы узнать, как.

Ответ 3

Я даю вам информацию о том, что я узнал о своем собственном желании, не отвечая на другой ответ (спасибо Basilevs, tploter очень), может быть, это помогает кому-то в том же случае:

  • Каждый раз, когда поток JVM блокирует файл исключительно, а также блокировку JVM некоторый объект Jave, например, я нахожу в своем случае:

    • sun.nio.fs.NativeBuffer
    • sun.nio.ch.Util $буферного кэша

    Итак, вам нужно просто найти этот заблокированный объект Java и проанализировать их, и вы обнаружите, что поток заблокировал ваш файл.

Я не уверен, что он работает, если файл просто открыт (без блокировки исключительно), но я уверен, что это работает, если файл заблокирован исключительно Thread (используя java.nio.channels.FileLock, java.nio.channels.FileChannel и т.д.)

  1. К сожалению, о Spark я обнаружил много других заблокированных объектов Hive (org.apache.hadoop.hive.ql.metadata.Hive, org.apache.hadoop.hive.metastore.ObjectStore, org.apache.hadoop.hive.ql.session.SessionState, org.apache.hadoop.hive.ql.metadata.Hive и т.д.), когда a Spark попытается удалить db.lck, и это означает, что Spark вообще не выключил Hive, прежде чем попытался удалить файлы Hive's. К счастью, эта проблема отсутствует в Linux OS (может быть Linux разрешена удалять заблокированные файлы).