繁体   English   中英

javaFX tableview和sqlite的内存泄漏

[英]Memory leak with javaFX tableview and sqlite

我正在JavaFX中创建发票系统,并使用带有“发票”对象的cellValueFactory将sqlite文件中的数据加载到表格视图中。 我决定加载最新的100个条目以获得更好的性能,然后通过调用sqlite数据库并检索上一个或下一个100个条目来在两个发票对象之间导航。 每次使用按钮并显示下一个100或-100时,它都会使用少量的内存,并且会被保留并且无法恢复,使用时间越长将成为一个问题。

我从其他线程听说过,这可能是由于侦听器中的强引用引起的,但我不确定。

没有错误,只有很小的内存泄漏,我无法修复。 我并没有显示我的所有代码,以希望不会使我的代码过于复杂,但是如果需要,请告诉我。

我是一名高中生,一直在教自己的Swing知识,然后在3个月前转到javaFX,我对使用GUI编程还是比较陌生,因此,如果我的代码风格有任何提示或任何不良习惯,请我喜欢学习。

主班(发射器)

package main;

import dbUtil.sqlitedb;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.stage.Stage;

import java.util.ArrayList;

//this class launches gui of invoices then distributes the data to the classes.
public class main extends Application {
    /*
    The main class is to access data and process it to and from the sqlitedb object and the invoiceController.
     */


    private boolean saved = true;
    private Parent root;
    private static sqlitedb dataBase;
    private static invoiceDataBaseController invoicesController;
    private Scene scene;
    private static ArrayList<invoiceOBJ> invoiceDataBase;
    private static int indexStart = 100;

    public static void main(String[] args) {

        launch(args);
    }


    public main() {
        invoiceDataBase = new ArrayList<>();



    }


    public void loadInvoices() {
        //dataBase.generateInvoices_test();
        invoiceDataBase = dataBase.listInvoices(indexStart);

    }

    public void setIndexStart(int amount) {
        //the indexStart for the sqlitedb class to use for portioning invoices.
        if (indexStart + amount == 0 || indexStart + amount < 0) {

        } else {
            indexStart += amount;
        }
    }
    // To reset table view's contents with new invoice page.
    public void refreshTable() {
        dataBase.connect();
        invoiceDataBase = dataBase.listInvoices(indexStart);
        invoicesController.addTableItems(invoiceDataBase);
        dataBase.disconnect();


    }



    @Override
    public void start(Stage primaryStage) throws Exception {

        initialize(primaryStage);


    }


    public Scene getScene() {
        return scene;
    }

    public void Loading(Alert alert) {
        // Loading alert before showing main gui
        alert.setHeaderText("Loading Files");
       // System.out.println("loading");
        alert.show();
    }

    public void initialize(Stage stage) {
        try {

            dataBase = new sqlitedb();
            //System.out.println("Loading files");
            //saveInvoicesAndCustomers(generateCustomersAndInvoices());


            FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));

            Alert alert = new Alert(Alert.AlertType.INFORMATION, "Loading data..");


            Loading(alert);


            loadInvoices();


            root = loader.load();

            invoicesController = loader.getController();
            invoicesController.setStageAndListeners(stage, this, invoiceDataBase);


            scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("resources/fxml.css").toExternalForm());
            stage.setTitle("JavaInvoice");
            stage.setScene(scene);


            stage.show();
            alert.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

sqliteDB对象中的相关方法:

sqlitedb中的此方法用于调用初始发票,然后稍后在控制器中调用更多方法。

     public ArrayList<invoiceOBJ> listInvoices(int indexStart) {
        //invoiceAmount  = invoices to index.
        ArrayList<invoiceOBJ> invoices = new ArrayList<>();
        Long time = System.currentTimeMillis(); //made to test indexing time
        //System.out.println(indexStart + "index start");
        try {
            //System.out.println("in list Invoices");

            stmt = c.createStatement();
            stmt.setFetchSize(1000);

            ResultSet rs = stmt.executeQuery(String.format("SELECT id,year,vin,carModel,condition,licenseState,regNum,stage,vehicleID,paintCode,dateOfInvoice," +
                    "bodyStyle,manufacturer, customerID  FROM invoices LIMIT 100  OFFSET (SELECT COUNT(*) FROM invoices)-%d", indexStart));
            int id;
            String customerID;
            String year;
            String vin;
            String carModel;
            String condition;
            String licenseState;
            String regNum;
            String stage;
            String vehicleID;
            String paintCode;
            String dateOfInvoice;
            String bodyStyle;
            String manufacturer;
            c.setAutoCommit(false);
            stmt.setFetchSize(1000);

            while (rs.next()) {


                id = rs.getInt(1);
                year = rs.getString(2);
                vin = rs.getString(3);
                carModel = rs.getString(4);
                condition = rs.getString(5);
                licenseState = rs.getString(6);
                regNum = rs.getString(7);
                stage = rs.getString(8);
                vehicleID = rs.getString(9);
                paintCode = rs.getString(10);
                dateOfInvoice = rs.getString(11);
                bodyStyle = rs.getString(12);
                manufacturer = rs.getString(13);
                customerID = rs.getString(14);


//                System.out.println(String.format("ID: %d , CustomerID: %s Year: %s, Vin: %s, \ncarModel %s, condition: %s, licenseState: %s \n" +
//                                "regNum: %s, stage: %s vehicleID: %s, paintCode: %s, dateOfInvoice: %s, bodyStyle:%s", id, customerID, year, vin, carModel
//                        , condition, licenseState, regNum, stage, vehicleID, paintCode, dateOfInvoice, bodyStyle));


                //add to invoice list.

                invoices.add(new invoiceOBJ(id,
                        carModel, manufacturer,
                        vin, condition, licenseState,
                        regNum, stage, vehicleID, paintCode,
                        bodyStyle, year, dateOfInvoice, findCustomer(customerID)));


                // System.out.println("added A customer");


            }
            stmt.close();
            rs.close();
            c.close();
            //System.out.println("load complete..");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // System.out.println(System.currentTimeMillis() - time);

        return invoices;

    }
public HashMap<String, String> findCustomer(String id) {

    String sql = String.format("SELECT firstName, lastName,id, date FROM customers WHERE id=%s", id);
    HashMap<String, String> customerData = new HashMap<>();
    try {

        Statement stmt = c.createStatement();
        ResultSet data = stmt.executeQuery(sql);


        customerData.put("firstName", data.getString("firstName"));
        customerData.put("lastName", data.getString("lastName"));
        customerData.put("date", data.getString("date"));
        customerData.put("id", data.getString("id"));

        stmt.close();
        data.close();

    } catch (Exception e) {
        e.printStackTrace();
    }

    return customerData;
}

invoiceController对象:

package main;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class invoiceDataBaseController implements Initializable {
    main main;
    @FXML
    private Button next = new Button();

    private invoiceLoader customerPage;
    @FXML
    private TableView<invoiceOBJ> invoices = new TableView<>();
    @FXML
    private Button back = new Button();
    @FXML
    private TextField searchField = new TextField();
    @FXML
    private Button confirmButton = new Button();
    @FXML
    private StackPane root = new StackPane();
    @FXML
    private ChoiceBox choice = new ChoiceBox();
    private Stage mainStage;


    public invoiceDataBaseController() {
        customerPage = new invoiceLoader();

    }



    public TextField getSearchField() {
        return searchField;
    }



    //Setup the table of people
    public void setTable() {
        choice.getItems().addAll("ID", "FirstName", "LastName", "Date", "Manufacturer");
        //set specific columns to table.
        TableColumn<invoiceOBJ, String> id = new TableColumn<>("ID:");
        TableColumn<invoiceOBJ, String> firstName = new TableColumn<invoiceOBJ, String>("First Name");
        TableColumn<invoiceOBJ, String> lastName = new TableColumn<invoiceOBJ, String>("Last Name");
        TableColumn<invoiceOBJ, String> dateOfInvoice = new TableColumn<invoiceOBJ, String>("Date");
        TableColumn<invoiceOBJ, String> manufac = new TableColumn<invoiceOBJ, String>("Manufacturer");
        TableColumn<invoiceOBJ, String> model = new TableColumn<invoiceOBJ, String>("Model");
        TableColumn<invoiceOBJ, String> vinNum = new TableColumn<invoiceOBJ, String>("Last 6 VIN");

        invoices.setEditable(true);

        //get data from objects.
        id.setCellValueFactory(p -> p.getValue().getGraphicalData("id"));
        firstName.setCellValueFactory(param -> param.getValue().getCustomer().getFirstName());
        lastName.setCellValueFactory(cellData -> cellData.getValue().getCustomer().getLastName());
        dateOfInvoice.setCellValueFactory(param -> param.getValue().getGraphicalData("dateOfInvoice"));
        manufac.setCellValueFactory(param -> param.getValue().getGraphicalData("manufacturer"));
        model.setCellValueFactory(param -> param.getValue().getGraphicalData("carModel"));
        vinNum.setCellValueFactory(param -> param.getValue().getGraphicalData("vin"));

        //set how dates get sorted
        dateOfInvoice.setComparator((t, t1) -> {
            try {

                SimpleDateFormat format = new SimpleDateFormat("MMM/dd/yyyy");

                return Long.compare(format.parse(t).getTime(), format.parse(t1).getTime());
            } catch (ParseException p) {
                p.printStackTrace();
            }
            return -1;

        });
        id.setComparator(Comparator.comparingInt(Integer::parseInt));
        //end
        //Add values to table view columns and rows.


        //end
        invoices.getColumns().addAll(id, firstName, lastName, dateOfInvoice, manufac, model, vinNum);
        //  dateOfInvoice.setSortType(TableColumn.SortType.DESCENDING);
        id.setSortType(TableColumn.SortType.DESCENDING);

        invoices.getSortOrder().add(id);
        choice.setValue("FirstName");

        //end of making columns,



    }

    public void addTableItems(ArrayList<invoiceOBJ> invoiceDataBase) {
       // System.out.println(Arrays.toString(invoiceDataBase.toArray()) + " table");
        //add invoices list to observable list. (GUI format)
        ObservableList<invoiceOBJ> custList = FXCollections.observableArrayList(invoiceDataBase);
        //Put list to filtered List
        FilteredList<invoiceOBJ> flInvoiceOBJS = new FilteredList(custList, p -> true);

        //add search to filter list values.
        getSearchField().textProperty().addListener(((observable, oldValue, newValue) -> {

            switch (choice.getValue().toString()) {
                case "FirstName":
                    flInvoiceOBJS.setPredicate(p -> p.getCustomer().getFirstName().getValue().toLowerCase().contains(newValue.toLowerCase()));
                    break;
                case "LastName":
                    flInvoiceOBJS.setPredicate(p -> p.getCustomer().getLastName().getValue().toLowerCase().contains(newValue.toLowerCase()));
                    break;
                case "Date":

                    flInvoiceOBJS.setPredicate(p -> p.getGraphicalData("dateOfInvoice").getValue().toLowerCase().contains(newValue.toLowerCase()));
                    break;
                case "Manufacturer":
                    flInvoiceOBJS.setPredicate(p -> p.getGraphicalData("manufacturer").getValue().toLowerCase().contains(newValue.toLowerCase()));
                    break;
                case "ID":
                    flInvoiceOBJS.setPredicate(p -> p.getData().get("id").contains(newValue.toLowerCase()));
                    break;


            }

        }));
        //add search to choiceBox changes.
        choice.valueProperty().addListener(((observable, oldValue, newValue) -> {

            switch (choice.getValue().toString()) {
                case "FirstName":
                    flInvoiceOBJS.setPredicate(p -> p.getCustomer().getFirstName().getValue().toLowerCase().contains(getSearchField().getText().toLowerCase()));
                    break;
                case "LastName":
                    flInvoiceOBJS.setPredicate(p -> p.getCustomer().getLastName().getValue().toLowerCase().contains(getSearchField().getText().toLowerCase()));
                    break;
                case "Date":

                    flInvoiceOBJS.setPredicate(p -> p.getGraphicalData("dateOfInvoice").getValue().toLowerCase().contains(getSearchField().getText().toLowerCase()));
                    break;
                case "Manufacturer":
                    flInvoiceOBJS.setPredicate(p -> p.getData().get("manufacturer").contains(getSearchField().getText().toLowerCase()));
                    break;
                case "ID":
                    flInvoiceOBJS.setPredicate(p -> p.getData().get("manufacturer").contains(getSearchField().getText().toLowerCase()));
                    break;


            }

        }));


        //put filtered list through sorted list
        SortedList<invoiceOBJ> sortedCusts = new SortedList<>(flInvoiceOBJS);
        //add comparators (date)
        sortedCusts.comparatorProperty().bind(invoices.comparatorProperty());
        //finally, add the items to the table view to show
        invoices.setItems(sortedCusts);
    }

private class pageButtonListener implements EventHandler<MouseEvent>  {
        private main root;
        private int indexIncrementor;

        // for indexing new invoices in sqlitedb
    public pageButtonListener(main root, int indexIncrementor){
        this.root =root;
        this.indexIncrementor = indexIncrementor;

    }

    @Override
    public void handle(MouseEvent event) {
        root.setIndexStart(indexIncrementor);
        root.refreshTable();


    }
}
    public void setStageAndListeners(Stage prime, main root, ArrayList<invoiceOBJ> invoiceDataBase) {
        pageButtonListener listener = new pageButtonListener(root,100);
        pageButtonListener backButtonListener = new pageButtonListener(root,-100);
        main = root;
        mainStage = prime;
        setTable();
        addTableItems(invoiceDataBase);
        next.setOnMouseClicked(listener);
        back.setOnMouseClicked(backButtonListener);



    }

    public void getSelected() {
        invoiceOBJ invoice = invoices.getSelectionModel().getSelectedItem();
    }


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        confirmButton.setOnAction(e -> {
            try {

                invoiceOBJ selectedInvoiceOBJ = invoices.getSelectionModel().getSelectedItem();
                if (selectedInvoiceOBJ == null) {
                    Alert noCustomer = new Alert(Alert.AlertType.ERROR, "No Customer Selected!!");
                    noCustomer.setHeaderText("Error, missing invoiceOBJ");
                    noCustomer.show();
                } else {
                    //send user to view invoice INCOMPLETE
                    customerPage.initialize(mainStage, main, selectedInvoiceOBJ);
                }
            } catch (Exception err) {

                err.printStackTrace();
            }
        });

    }
}

InvoiceOBJ:

 package main;

import javafx.beans.property.SimpleStringProperty;
import java.util.HashMap;

public class invoiceOBJ {
    private HashMap<String, String> invoiceData;
    private int customerID;
    private String customerIDToString;
    private customer cust = new customer();

    public SimpleStringProperty getGraphicalData(String key){
        //return hashmap key value into graphical data.
        return new SimpleStringProperty(invoiceData.get(key));
    }


    public HashMap<String, String> getData() {
        return invoiceData;
    }
    public int getCustomerID(){
        return customerID;
    }
    public String getCustomerIDToString(){
        return customerIDToString;
    }
    public customer getCustomer(){
        return cust;
    }

    @Deprecated // reminder to change this god awful system of passing several variables.
    public invoiceOBJ(int idOfInvoice,
                      String carModel, String manufacturer,
                      String vin, String condition, String licenseState,
                      String regNum, String stage, String vehicleID, String paintCode,
                      String bodyStyle, String year, String dateOfInvoice, HashMap<String,String> customerData) {


        cust.setData(customerData);
        invoiceData = new HashMap<>();
        this.customerID = Integer.parseInt(customerData.get("id"));
        this.customerIDToString = customerData.get("id");
        invoiceData.put("id", Integer.toString(idOfInvoice));
        invoiceData.put("carModel", carModel);
        invoiceData.put("manufacturer", manufacturer);
        invoiceData.put("vin", vin);
        invoiceData.put("condition", condition);
        invoiceData.put("licenseState", licenseState);
        invoiceData.put("regNum", regNum);
        invoiceData.put("stage", stage);
        invoiceData.put("vehicleID", vehicleID);
        invoiceData.put("paintCode", paintCode);
        invoiceData.put("bodyStyle", bodyStyle);
        invoiceData.put("year", year);
        invoiceData.put("dateOfInvoice", dateOfInvoice);

        //put into hashmap for easy access of data later.

    }
    public invoiceOBJ(HashMap<String, String> data, HashMap<String,String> customerData) {
        invoiceData = new HashMap<>(data);
        cust.setData(customerData);
        this.customerID = Integer.parseInt(customerData.get("id"));
        this.customerIDToString = Integer.toString(customerID);


    }
}

因为每次添加新数据时都在创建新的ChangeListener所以存在内存泄漏。 这是有问题的块:

public void addTableItems(ArrayList<invoiceOBJ> invoiceDataBase) {
    // System.out.println(Arrays.toString(invoiceDataBase.toArray()) + " table");
    //add invoices list to observable list. (GUI format)
    ObservableList<invoiceOBJ> custList = FXCollections.observableArrayList(invoiceDataBase);
    //Put list to filtered List
    FilteredList<invoiceOBJ> flInvoiceOBJS = new FilteredList(custList, p -> true);

    //add search to filter list values.
    getSearchField().textProperty().addListener(((observable, oldValue, newValue) -> {
        switch (choice.getValue().toString()) {
            case "FirstName":
                flInvoiceOBJS.setPredicate(p -> p.getCustomer().getFirstName().getValue().toLowerCase().contains(newValue.toLowerCase()));
                break;
            case "LastName":
                flInvoiceOBJS.setPredicate(p -> p.getCustomer().getLastName().getValue().toLowerCase().contains(newValue.toLowerCase()));
                break;
            case "Date":

                flInvoiceOBJS.setPredicate(p -> p.getGraphicalData("dateOfInvoice").getValue().toLowerCase().contains(newValue.toLowerCase()));
                break;
            case "Manufacturer":
                flInvoiceOBJS.setPredicate(p -> p.getGraphicalData("manufacturer").getValue().toLowerCase().contains(newValue.toLowerCase()));
                break;
            case "ID":
                flInvoiceOBJS.setPredicate(p -> p.getData().get("id").contains(newValue.toLowerCase()));
                break;
        }

    }));
    //add search to choiceBox changes.
    choice.valueProperty().addListener(((observable, oldValue, newValue) -> {

        switch (choice.getValue().toString()) {
            case "FirstName":
                flInvoiceOBJS.setPredicate(p -> p.getCustomer().getFirstName().getValue().toLowerCase().contains(getSearchField().getText().toLowerCase()));
                break;
            case "LastName":
                flInvoiceOBJS.setPredicate(p -> p.getCustomer().getLastName().getValue().toLowerCase().contains(getSearchField().getText().toLowerCase()));
                break;
            case "Date":

                flInvoiceOBJS.setPredicate(p -> p.getGraphicalData("dateOfInvoice").getValue().toLowerCase().contains(getSearchField().getText().toLowerCase()));
                break;
            case "Manufacturer":
                flInvoiceOBJS.setPredicate(p -> p.getData().get("manufacturer").contains(getSearchField().getText().toLowerCase()));
                break;
            case "ID":
                flInvoiceOBJS.setPredicate(p -> p.getData().get("manufacturer").contains(getSearchField().getText().toLowerCase()));
                break;


        }
    }));

    //put filtered list through sorted list
    SortedList<invoiceOBJ> sortedCusts = new SortedList<>(flInvoiceOBJS);
    //add comparators (date)
    sortedCusts.comparatorProperty().bind(invoices.comparatorProperty());
    //finally, add the items to the table view to show
    invoices.setItems(sortedCusts);
}

让我们来看看为什么要进行逐步分析。

  1. 您创建了一个名为custList的新ObservableList ,然后用FilteredList对其进行包装。
  2. 您将ChangeListener添加到两个控件上的两个属性。 在这些侦听器内部,您调用了FilteredList.setPredicate(Predicate) 当您以这种方式引用FilteredListChangeListener必须保留对FilteredList的强引用,而无需保留强引用,如果列表似乎已被垃圾回收,它将在运行时意外抛出NullPointerException
  3. 您调用了TableView.setItems()以应用新的过滤后的排序项。

到目前为止,一切似乎都可以进行,但是...

您单击“下一步”并加载了一组新数据,该数据再次调用了此确切方法。

这意味着您将创建另一个 ObservableList ,并包装另一个 FilteredList 然后再次ChangeListener (x2)添加到相同的属性。 侦听器将保留新FilteredList的引用。 然后, 再次设置表格的项目。

再次调用TableView.setItems()时,实际上是在丢弃旧列表。 您以为最终会被垃圾回收,但是...您的听众仍然在这两个属性中! 回想一下,所有这些旧侦听器都保存着旧列表的引用。

您只需一次创建所有这些步骤, 而无需添加任何数据 数据输入后,只需调用TableView.getItems().setAll(newList) 这样,您无需再次重做所有内容并保留其他引用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM