【问题标题】:Invalid pointer when deleting GLib.Tree elements in Vala在 Vala 中删除 GLib.Tree 元素时指针无效
【发布时间】:2018-05-05 22:32:06
【问题描述】:

此代码导致以下错误

free(): invalid pointer

Online example

using GLib;
using GLib.Random;

// Global variable
Tree<string, Tree<int, string> > mainTree;

public static int main (string[] args) {
    // Initiate random seed
    Random.set_seed ((uint32) get_monotonic_time());
    // mainTree initialization
    mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free, free);
    // Random sized for loop
    for (int i = 0; i < int_range (1000, 10001); i++) {
        // If a condition is met (i is even)
        if (i % 2 == 0) {
            // Create a Tree to nest onto mainTree
            Tree<int, string> treeToNest = new Tree<int, string>.full (treeToNestCompareDataFunction, free, free);
            // Insert random content into treeToNest
            treeToNest.insert (int_range (0, 101), randomString ());
            // Insert the tree onto mainTree
            mainTree.insert (randomString (), treeToNest);
        }
    }

    // Empty the tree
    mainTree.@foreach ((mainTreeKey, mainTreeValue) => {
        mainTree.remove (mainTreeKey); // This line causes a free(): invalid pointer error
        return false;
    });

    return 0;
}

public string randomString () {
    string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    string stringToReturn = "";
    // Create a random 8 character string
    for (int i = 0; i < 8; i++) {
        stringToReturn += charset[int_range (0, charset.length)].to_string ();
    }
    return stringToReturn;
}

public int treeToNestCompareDataFunction (int a, int b) {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0; 
}

public int mainTreeCompareDataFunction (string a, string b) {
    return strcmp (a, b);
}

我怀疑这是因为树中有一个嵌套的GLib.Tree,而free() 不能用于这些对象。如果我使用null 代替mainTree 的值的destroy 函数,则不会发生崩溃,但是如果我要重用mainTree 变量,则会产生内存泄漏。

有没有办法清空树并释放内存?

【问题讨论】:

    标签: memory-management glib vala


    【解决方案1】:

    您可以编写一个 lambda,使用所有权转移删除子树。

        mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free,
            (data) => {
              var tree = (Tree<string, Tree<int, string>>) data;
              // Ownership is transfered to this local var which calls unref when it goes out of scope
              var tree2 = (owned) tree;
            });
    

    这里是完整的代码:

    using GLib;
    using GLib.Random;
    
    // Global variable
    Tree<string, Tree<int, string> > mainTree;
    
    public static int main (string[] args) {
        // Initiate random seed
        Random.set_seed ((uint32) get_monotonic_time());
        // mainTree initialization
        mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free,
            (data) => {
              var tree = (Tree<string, Tree<int, string>>) data;
              // Ownership is transfered to this local var which calls unref when it goes out of scope
              var tree2 = (owned) tree;
            });
        // Random sized for loop
        for (int i = 0; i < int_range (1000, 10001); i++) {
            // If a condition is met (i is even)
            if (i % 2 == 0) {
                // Create a Tree to nest onto mainTree
                Tree<int, string> treeToNest = new Tree<int, string>.full (treeToNestCompareDataFunction, null, free);
                // Insert random content into treeToNest
                treeToNest.insert (int_range (0, 101), randomString ());
                // Insert the tree onto mainTree
                mainTree.insert (randomString (), treeToNest);
            }
        }
    
        // Empty the tree
        mainTree.@foreach ((mainTreeKey, mainTreeValue) => {
            mainTree.remove (mainTreeKey);
            return false;
        });
    
        return 0;
    }
    
    public string randomString () {
        string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        string stringToReturn = "";
        // Create a random 8 character string
        for (int i = 0; i < 8; i++) {
            stringToReturn += charset[int_range (0, charset.length)].to_string ();
        }
        return stringToReturn;
    }
    
    public int treeToNestCompareDataFunction (int a, int b) {
        if (a < b) return -1;
        if (a > b) return 1;
        return 0; 
    }
    
    public int mainTreeCompareDataFunction (string a, string b) {
        return strcmp (a, b);
    }
    

    我会避免在 Vala 中使用 GLib.Tree 类,因为很难正确进行内存管理。您应该考虑改用Gee.TreeMap

    【讨论】:

    • 感谢您的回答!这样效果更好。我没有使用 Gee.TreeMap 因为它给了我内存泄漏,因为嵌套的 Gee.TreeMap 被存储为 gconstpointer 由于 Gee 库对键和值如何存储在映射中的实现并且它不会释放清除树时它指向的内存。在gee.h 文件中可以看到,它使用 gconstpointers 将键和数据存储在树上。相同的代码,但使用 Gee.TreeMap 显示 valgrind 中“可能丢失”的值,以及将数据插入树的更多步骤,这会减慢程序的速度。
    • 好的,如果你还没有写过,那么你应该为 libgee 写一个错误报告。
    【解决方案2】:

    找到了进行手动内存管理的解决方案。首先,使嵌套树成为指针。其次,将null 传递给用于指定释放嵌套树的函数的参数。第三,将null 传递给用于指定释放嵌套树的int 键的函数的参数。最后,使用delete 关键字在清空主树时减少对嵌套树的引用。

    由于在 Vala 生成的 C 代码中手动调用delete这里特别调用g_tree_unref,因此在此代码中对嵌套树的引用将下降为 0,所有键和嵌套树的值被破坏并释放树分配的所有内存。这样就不会再发生无效指针错误,也不会发生内存泄漏。

    using GLib;
    using GLib.Random;
    
    // Global variable
    Tree<string, Tree<int, string>* > mainTree;
    
    public static int main (string[] args) {
        // Initiate random seed
        Random.set_seed ((uint32) get_monotonic_time());
        // mainTree initialization
        mainTree = new Tree<string, Tree<int, string> >.full (mainTreeCompareDataFunction, free, null);
        // Random sized for loop
        for (int i = 0; i < int_range (1000, 10001); i++) {
            // If a condition is met (i is even)
            if (i % 2 == 0) {
                // Create a Tree to nest onto mainTree
                Tree<int, string>* treeToNest = new Tree<int, string>.full (treeToNestCompareDataFunction, null, free);
                // Insert random content into treeToNest
                treeToNest->insert (int_range (0, 101), randomString ());
                // Insert the tree onto mainTree
                mainTree.insert (randomString (), treeToNest);
            }
        }
    
        // Empty the tree
        mainTree.@foreach ((mainTreeKey, mainTreeValue) => {
            delete mainTree.lookup (mainTreeKey);
            mainTree.remove (mainTreeKey);
            return false;
        });
    
        return 0;
    }
    
    public string randomString () {
        string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        string stringToReturn = "";
        // Create a random 8 character string
        for (int i = 0; i < 8; i++) {
            stringToReturn += charset[int_range (0, charset.length)].to_string ();
        }
        return stringToReturn;
    }
    
    public int treeToNestCompareDataFunction (int a, int b) {
        if (a < b) return -1;
        if (a > b) return 1;
        return 0; 
    }
    
    public int mainTreeCompareDataFunction (string a, string b) {
        return strcmp (a, b);
    }
    

    来源:

    【讨论】:

    • 虽然手动内存管理有效,但在 Vala 中它被认为是最后的解决方案。
    猜你喜欢
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多