Out of memory exception with Vaadin 8 Grid

73 Views Asked by At

I'm facing a memory issue with the Grid that I'm using with vaadin 8. I have a tab that contains User details in a Grid component. When we try to load the grid we are facing an issue and the application got crashed because of memory.

Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
Caused by: org.postgresql.util.PSQLException: Ran out of memory retrieving query results.

Mainly this issue is occurring when more than 2 users try to load the tab at the same time. I checked the database to see whether the issue is in retrieving the data from bd. I didn't see any issues and the data is loading pretty quickly. But in UI the data is loading slowly.

enter image description here enter image description here

I'm adding the source code of my User.view file here.

package com.iodine.imc.ui.views.sections.admin.users;

import com.iodine.imc.model.users.User;
import com.iodine.imc.security.PermissionConstants;
import com.iodine.imc.ui.Sections;
import com.iodine.imc.ui.views.components.HeaderComponent;
import com.iodine.imc.ui.views.sections.AbstractView;
import com.vaadin.data.ValueContext;
import com.vaadin.data.provider.GridSortOrder;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.ui.Button;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.Grid;
import com.vaadin.ui.TextField;
import com.vaadin.ui.renderers.HtmlRenderer;
import com.vaadin.ui.themes.ValoTheme;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.vaadin.haijian.Exporter;
import org.vaadin.spring.sidebar.annotation.SideBarItem;
import java.util.List;

/**
 * Admin Users View
 */
@SpringView(name = UsersView.VIEW_NAME)
@SideBarItem(sectionId = Sections.ADMIN, caption = "Users", order = 5)
@Secured(PermissionConstants.ADMINISTER_USERS)
public class UsersView extends AbstractView
{
    private static final long serialVersionUID = -3688602854211286135L;

    static final String VIEW_NAME = "usersView";

    private static final String COLUMN_LAST_NAME = "lastName";
    private static final String COLUMN_FIRST_NAME = "firstName";
    private static final String COLUMN_USERNAME = "username";
    private static final String COLUMN_EMAIL = "email";
    private static final String COLUMN_ROLES = "Roles";
    private static final String COLUMN_ENABLED = "Enabled";

    private final UsersViewChangeListener listener;

    private TextField userFilter;
    private Grid<User> usersGrid;
    private transient Object item;

    /**
     * New Instance
     */
    @Autowired
    public UsersView(UsersViewChangeListener listener)
    {
        this.listener = listener;
    }

    @Override
    public void initializedView()
    {
        listener.viewInitialized(this);
    }

    @Override
    public void destroyView() {
        //Not applicable
    }

    @Override
    protected void buildLayout()
    {
        setSizeFull();
        setMargin(true);
        setSpacing(false);
        addStyleName("layoutMargin");

        HeaderComponent header = new HeaderComponent("Users");

        CssLayout filterSection = new CssLayout();
        filterSection.setWidth(100, Unit.PERCENTAGE);
        filterSection.addStyleName("view-filter");

        userFilter = new TextField();
        userFilter.setPlaceholder("Search for users");
        userFilter.setWidth(250, Unit.PIXELS);
        userFilter.addValueChangeListener(textChangeEvent -> listener.filterUsers(textChangeEvent.getValue()));

        Button newUser = new Button("New User");
        newUser.setSizeUndefined();
        newUser.setStyleName(ValoTheme.BUTTON_FRIENDLY);
        newUser.addStyleName("margin-left");
        newUser.addClickListener(clickEvent ->
        {
            toggleUserDetails();
            User newUser1 = new User();
            usersGrid.setItems(newUser1);
            item = newUser1;
            toggleUserDetails();
        });

        createUsersGrid();

        Button export = new Button("Export");
        export.setIcon(VaadinIcons.LIST_UL);
        export.addStyleName("move-right");
        StreamResource excelStreamResource = new StreamResource(() -> Exporter.exportAsExcel(usersGrid), "Users Report.xlsx");
        FileDownloader excelFileDownloader = new FileDownloader(excelStreamResource);
        excelFileDownloader.extend(export);
        header.addComponent(export);

        filterSection.addComponents(userFilter, newUser);
        addComponents(header, filterSection, usersGrid);
        setExpandRatio(usersGrid, 1f);

    }

    @Override
    protected void enterView(ViewChangeListener.ViewChangeEvent viewChangeEvent)
    {
        listener.refreshUsers();
    }

    private void createUsersGrid()
    {
        usersGrid = new Grid(User.class);

        usersGrid.setSizeFull();
        usersGrid.setColumnReorderingAllowed(false);
        usersGrid.setSelectionMode(Grid.SelectionMode.SINGLE);

        usersGrid.setColumns(COLUMN_LAST_NAME, COLUMN_FIRST_NAME, COLUMN_USERNAME, COLUMN_EMAIL);
        usersGrid.getColumn(COLUMN_USERNAME).setWidth(160);
        usersGrid.getColumn(COLUMN_EMAIL).setWidth(250);
        usersGrid.addColumn(user ->
        {
            UsersViewRoleConverter roleConverter = new UsersViewRoleConverter();
            return roleConverter.convertToPresentation(user.getRoles(), new ValueContext());
        }).setCaption(COLUMN_ROLES);
        usersGrid.addColumn(user -> (user.isEnabled() ? VaadinIcons.CHECK_SQUARE_O : VaadinIcons.THIN_SQUARE).getHtml(),
            new HtmlRenderer()).setCaption(COLUMN_ENABLED);

        usersGrid.setSortOrder(GridSortOrder.asc(usersGrid.getColumn(COLUMN_LAST_NAME)).thenAsc(usersGrid.getColumn(COLUMN_FIRST_NAME)));

        // Disallow resizing to prevent scroll
        for (Grid.Column column : usersGrid.getColumns())
        {
            column.setResizable(false);
        }

        usersGrid.setDetailsGenerator(rowRef->{
            UserDetailsView detailsView = new UserDetailsView(listener);
            if(rowRef != null) {
                detailsView.edit(rowRef);
                return detailsView;
            }
            else {
                return detailsView;
            }
        });
        usersGrid.getEditor().setEnabled(false);

        usersGrid.addItemClickListener(newGridDetailsListener(usersGrid));
    }

    String getFilter()
    {
        return userFilter.getValue();
    }

    void toggleUserDetails()
    {
        toggleGridDetails(usersGrid, item);
        item = null;
    }

    void setDataSource(List<User> users)
    {
        usersGrid.setItems(users);
    }

}
1

There are 1 best solutions below

0
ollitietavainen On

You're not sharing enough code to fully understand what is going on. The exception is coming from the database, so the issue is in your code that executes database queries. One suspicious piece of code I can immediately spot is this:

        usersGrid.addColumn(user ->
        {
            UsersViewRoleConverter roleConverter = new UsersViewRoleConverter();
            return roleConverter.convertToPresentation(user.getRoles(), new ValueContext());
        }).setCaption(COLUMN_ROLES);

If user.getRoles() is evaluated lazily, it means every time a row is rendered, there's an additional database query to fetch the roles, and this can't be efficient.